first commit
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Controller;
|
||||
|
||||
use KupShop\CatalogBundle\Query\Search;
|
||||
use KupShop\KupShopBundle\Routing\AdminRoute;
|
||||
use Query\Operator as Op;
|
||||
use Query\QueryBuilder;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SectionsAutocompleteController extends AbstractController
|
||||
{
|
||||
#[AdminRoute(path: '/autocomplete/sections/{id}', methods: ['GET'])]
|
||||
public function getSubsections(Request $request, ?int $id = null): Response
|
||||
{
|
||||
if ($request->query->has('term')) {
|
||||
return $this->json($this->autocomplete($request->query->get('term')));
|
||||
}
|
||||
|
||||
if ($request->query->get('all_subsections') == 1) {
|
||||
$qb = $this->getRecursiveQuery($id, 's');
|
||||
} else {
|
||||
$qb = sqlQueryBuilder()
|
||||
->from('sections_relation', 'ss')
|
||||
->leftJoin('ss', 'sections', 's', 's.id = ss.id_section');
|
||||
|
||||
if (!isset($id)) {
|
||||
$qb->andWhere('s.id IN (SELECT DISTINCT id_section FROM sections_relation WHERE id_topsection IS NULL OR id_topsection = 0)');
|
||||
} else {
|
||||
$qb->andWhere(Op::equals(['id_topsection' => $id]));
|
||||
}
|
||||
}
|
||||
|
||||
$qb->addSelect('s.id', 's.name', $this->hasSubsections('s.id'))
|
||||
->andWhere("s.figure = 'Y'")
|
||||
->addOrderBy('position');
|
||||
|
||||
return $this->json($qb->execute()->fetchAllAssociative());
|
||||
}
|
||||
|
||||
public function autocomplete(string $term): array
|
||||
{
|
||||
$qb = $this->getRecursiveQuery()
|
||||
->select(
|
||||
'cte.id',
|
||||
'cte.name',
|
||||
'cte.path as full_path',
|
||||
$this->hasSubsections('cte.id'),
|
||||
)
|
||||
->andWhere(Search::searchFields($term, [['field' => 'cte.path', 'match' => 'both']], 'OR'))
|
||||
->setMaxResults(30);
|
||||
|
||||
return $qb->execute()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
protected function getRecursiveQuery(?int $idTopSection = null, string $alias = 'cte'): QueryBuilder
|
||||
{
|
||||
$whereSection = $idTopSection ? 'id_topsection = :idTopSection' : 'id_topsection IS NULL';
|
||||
|
||||
$recursiveQuery = "
|
||||
WITH RECURSIVE cte (id, id_topsection, path, name, figure, position, depth, full_position) as (
|
||||
SELECT id_section,
|
||||
id_topsection,
|
||||
(SELECT name FROM sections WHERE id = id_section LIMIT 1) as path,
|
||||
(SELECT name FROM sections WHERE id = id_section LIMIT 1) as name,
|
||||
(SELECT figure FROM sections WHERE id = id_section LIMIT 1) as figure,
|
||||
position,
|
||||
1 AS depth,
|
||||
CAST(LPAD(position, 5, 0) AS CHAR(500)) AS full_position
|
||||
FROM sections_relation
|
||||
WHERE {$whereSection}
|
||||
UNION ALL
|
||||
SELECT sr.id_section,
|
||||
sr.id_topsection,
|
||||
CONCAT(cte.path,' > ',(SELECT name FROM sections WHERE id = sr.id_section LIMIT 1)) as path,
|
||||
(SELECT name FROM sections WHERE id = sr.id_section LIMIT 1) as name,
|
||||
(SELECT figure FROM sections WHERE id = sr.id_section LIMIT 1) as figure,
|
||||
sr.position,
|
||||
depth + 1,
|
||||
CONCAT(full_position, '/', LPAD(sr.position, 5, 0))
|
||||
FROM sections_relation sr
|
||||
INNER JOIN cte
|
||||
on sr.id_topsection = cte.id
|
||||
) SELECT * FROM cte";
|
||||
|
||||
$qb = sqlQueryBuilder()->from('('.$recursiveQuery.')', $alias);
|
||||
|
||||
if ($idTopSection !== null) {
|
||||
$qb->setParameter('idTopSection', $idTopSection);
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
private function hasSubsections(string $idField = 'id', string $as = 'has_subsections'): string
|
||||
{
|
||||
return "EXISTS (
|
||||
SELECT *
|
||||
FROM sections_relation _ss
|
||||
LEFT JOIN sections _s ON _s.id = _ss.id_section
|
||||
WHERE {$idField} = _ss.id_topsection
|
||||
AND _s.figure = 'Y'
|
||||
) AS {$as}";
|
||||
}
|
||||
}
|
||||
56
bundles/KupShop/CatalogBundle/Admin/ProductsFilter.php
Normal file
56
bundles/KupShop/CatalogBundle/Admin/ProductsFilter.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin;
|
||||
|
||||
use KupShop\CatalogBundle\ProductList\ProductList;
|
||||
use KupShop\CatalogBundle\Util\ProductsFilterSpecs;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
|
||||
class ProductsFilter extends \Window
|
||||
{
|
||||
protected $template = 'window/productsFilter.tpl';
|
||||
|
||||
public function get_vars()
|
||||
{
|
||||
$vars = parent::get_vars();
|
||||
|
||||
$vars['filter'] = json_decode(getVal('filter'), true);
|
||||
|
||||
$vars['blocekProducts'] = getVal('callback') == 'blocek_setProducts';
|
||||
$vars['productsSortable'] = $vars['blocekProducts'];
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function handleLoadProducts()
|
||||
{
|
||||
$filter = getVal('filter');
|
||||
$filter = \KupShop\AdminBundle\Admin\ProductsFilter::cleanFilter($filter);
|
||||
|
||||
$productList = ServiceContainer::getService(ProductList::class);
|
||||
$productsFilterSpecs = ServiceContainer::getService(ProductsFilterSpecs::class);
|
||||
$filterSpecs = $productsFilterSpecs->getSpecs($filter);
|
||||
$productList->andSpec($filterSpecs);
|
||||
$productList->fetchImages('admin');
|
||||
$productList->limit(20);
|
||||
|
||||
$result = [];
|
||||
$result['count'] = 0;
|
||||
$result['filter'] = json_encode($filter);
|
||||
foreach ($productList->getProducts($result['count']) as $product) {
|
||||
/* @var $product \Product */
|
||||
$result['products'][] = [
|
||||
'id' => $product->id,
|
||||
'title' => $product->title,
|
||||
'code' => $product->code,
|
||||
'inStore' => $product->inStore,
|
||||
'photo' => $product->image['src'] ?? false,
|
||||
];
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
return ProductsFilter::class;
|
||||
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
use KupShop\CatalogBundle\Repository\SectionsRepository;
|
||||
use KupShop\CatalogBundle\Section\SectionFilter;
|
||||
use KupShop\CatalogBundle\Util\FilterUtil;
|
||||
use Query\Operator;
|
||||
use Query\QueryBuilder;
|
||||
|
||||
class OrderableSectionFilters extends WindowTab
|
||||
{
|
||||
protected $title = 'flapSearch';
|
||||
protected $template = 'window/sections.filters.tpl';
|
||||
|
||||
/** @var 'Y'|'N' */
|
||||
private string $inherit;
|
||||
|
||||
public function __construct(
|
||||
private readonly FilterUtil $filterUtil,
|
||||
private readonly SectionsRepository $sectionsRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getVars($smarty_tpl_vars): array
|
||||
{
|
||||
return [
|
||||
'ID' => $this->getID(),
|
||||
'filterItems' => $this->getFilterItems(),
|
||||
'type' => $this->getWindow()->getName(),
|
||||
'inherit' => $this->getInherit(),
|
||||
];
|
||||
}
|
||||
|
||||
public function handleUpdate(): void
|
||||
{
|
||||
$data = getVal('data');
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
|
||||
$inherit = getVal('inherit', $data, 'N');
|
||||
$this->saveInherit($inherit);
|
||||
|
||||
$filters = getVal('filters', $data);
|
||||
if ($inherit === 'Y') {
|
||||
foreach ($filters as &$filter) {
|
||||
$filter['enabled'] = 'N';
|
||||
}
|
||||
}
|
||||
|
||||
if ($filters === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sqlGetConnection()->transactional(function () use ($filters) {
|
||||
$this->filterUtil->updateSectionFilters($filters);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Driver\Exception
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*/
|
||||
public function getFilterItems(): array
|
||||
{
|
||||
if ($this->isSection()) {
|
||||
$section = $this->sectionsRepository->findById((int) $this->getID());
|
||||
$filterItems = $this->filterUtil->getSectionFilters($section, null);
|
||||
} else {
|
||||
$filterItems = $this->filterUtil->getSectionFilters(null, (int) $this->getID());
|
||||
}
|
||||
|
||||
$customFilters = [
|
||||
SectionFilter::TYPE_PRICE_RANGE => false,
|
||||
SectionFilter::TYPE_SEARCH => false,
|
||||
SectionFilter::TYPE_IN_STORE => false,
|
||||
];
|
||||
|
||||
if (findModule(\Modules::LABELS)) {
|
||||
$customFilters[SectionFilter::TYPE_LABELS] = false;
|
||||
}
|
||||
|
||||
if (findModule(\Modules::SELLERS)) {
|
||||
$customFilters[SectionFilter::TYPE_SELLERS] = false;
|
||||
}
|
||||
|
||||
if (!$this->isProducer()) {
|
||||
$customFilters[SectionFilter::TYPE_PRODUCERS] = false;
|
||||
}
|
||||
|
||||
foreach ($filterItems as $filterItem) {
|
||||
if (array_key_exists($filterItem['type'], $customFilters)) {
|
||||
$customFilters[$filterItem['type']] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($customFilters as $type => $has) {
|
||||
if ($has) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filterItems[] = [
|
||||
'type' => $type,
|
||||
'indexing_allowed' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$parameters = $this->createParametersQueryBuilder();
|
||||
$variations = $this->createVariationsQueryBuilder();
|
||||
|
||||
return array_merge(
|
||||
$filterItems,
|
||||
$parameters->execute()->fetchAllAssociative(),
|
||||
$variations->execute()->fetchAllAssociative(),
|
||||
);
|
||||
}
|
||||
|
||||
private function createVariationsQueryBuilder(): QueryBuilder
|
||||
{
|
||||
$variationType = SectionFilter::TYPE_VARIATION;
|
||||
$variationsSelects = [
|
||||
'id' => 'NULL',
|
||||
'id_source_section' => 'NULL',
|
||||
'id_source_producer' => 'NULL',
|
||||
'id_parameter' => 'NULL',
|
||||
'id_variation_label' => 'pvcl.id',
|
||||
'type' => "'{$variationType}'",
|
||||
'position' => 'NULL',
|
||||
'enabled' => "'N'",
|
||||
'name' => 'pvcl.label',
|
||||
];
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$variationsSelects['indexing_allowed'] = '0';
|
||||
}
|
||||
|
||||
if (findModule(\Modules::INDEXED_FILTER) && $this->isSection()) {
|
||||
$variationsSelects = array_merge($variationsSelects, [
|
||||
'indexing' => 'pvs.indexing',
|
||||
'to_title' => 'pvs.to_title',
|
||||
]);
|
||||
}
|
||||
|
||||
if (findModule(\Modules::CONVERTORS) && $this->isSection()) {
|
||||
$variationsSelects['convertor'] = 'pvs.convertor';
|
||||
}
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$condition = 'id_source_producer = :id_producer';
|
||||
} else {
|
||||
$condition = 'id_source_section = :id_section';
|
||||
}
|
||||
|
||||
$variations = sqlQueryBuilder()
|
||||
->select($this->sqlAliasedArray($variationsSelects))
|
||||
->from('products_variations_choices_labels', 'pvcl')
|
||||
->andWhere("NOT EXISTS (SELECT 1 FROM sections_filters WHERE id_variation_label = pvcl.id AND {$condition})");
|
||||
|
||||
if ($this->isSection()) {
|
||||
$variations->leftJoin('pvcl',
|
||||
'products_variations_sections',
|
||||
'pvs',
|
||||
'pvs.id_label = pvcl.id AND pvs.id_section = :id_section',
|
||||
);
|
||||
|
||||
$variations->setParameter('id_section', $this->getID());
|
||||
} else {
|
||||
$variations->setParameter('id_producer', $this->getID());
|
||||
}
|
||||
|
||||
return $variations;
|
||||
}
|
||||
|
||||
private function createParametersQueryBuilder(): QueryBuilder
|
||||
{
|
||||
$parameterType = SectionFilter::TYPE_PARAMETER;
|
||||
$parameterName = <<<__SQL__
|
||||
IF(p.unit <> '',
|
||||
CONCAT(
|
||||
p.name,
|
||||
' [', REPLACE(p.unit, '|', ', '), ']'
|
||||
),
|
||||
p.name
|
||||
)
|
||||
__SQL__;
|
||||
|
||||
$parametersSelects = [
|
||||
'id' => 'NULL',
|
||||
'id_source_section' => 'NULL',
|
||||
'id_source_producer' => 'NULL',
|
||||
'id_parameter' => 'p.id',
|
||||
'id_variation_label' => 'NULL',
|
||||
'type' => "'{$parameterType}'",
|
||||
'position' => 'NULL',
|
||||
'enabled' => "'N'",
|
||||
'name' => $parameterName,
|
||||
'value_type' => 'p.value_type',
|
||||
];
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$parametersSelects['indexing_allowed'] = '0';
|
||||
}
|
||||
|
||||
if (findModule(\Modules::INDEXED_FILTER) && $this->isSection()) {
|
||||
$parametersSelects = array_merge($parametersSelects, [
|
||||
'indexing' => 'ps.indexing',
|
||||
'to_title' => 'ps.to_title',
|
||||
]);
|
||||
}
|
||||
|
||||
if (findModule(\Modules::CONVERTORS) && $this->isSection()) {
|
||||
$parametersSelects['convertor'] = 'NULL';
|
||||
}
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$condition = 'id_source_producer = :id_producer';
|
||||
} else {
|
||||
$condition = 'id_source_section = :id_section';
|
||||
}
|
||||
|
||||
$parameters = sqlQueryBuilder()
|
||||
->select($this->sqlAliasedArray($parametersSelects))
|
||||
->from('parameters', 'p')
|
||||
->andWhere("p.figure = 'Y'")
|
||||
->andWhere("NOT EXISTS (SELECT 1 FROM sections_filters WHERE id_parameter = p.id AND {$condition})");
|
||||
|
||||
if ($this->isSection()) {
|
||||
$parameters->leftJoin('p',
|
||||
'parameters_sections',
|
||||
'ps',
|
||||
'ps.id_parameter = p.id AND ps.id_section = :id_section',
|
||||
);
|
||||
$parameters->setParameter('id_section', $this->getID());
|
||||
} else {
|
||||
$parameters->leftJoin('p',
|
||||
'parameters_producers',
|
||||
'ps',
|
||||
'ps.id_parameter = p.id AND ps.id_producer = :id_producer',
|
||||
);
|
||||
$parameters->setParameter('id_producer', $this->getID());
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
private function sqlAliasedArray(array $selects): array
|
||||
{
|
||||
return array_map(
|
||||
static fn ($alias, $field) => "{$field} AS {$alias}",
|
||||
array_keys($selects),
|
||||
$selects,
|
||||
);
|
||||
}
|
||||
|
||||
public static function isAllowed(): bool
|
||||
{
|
||||
return FilterUtil::useOrderableFilters();
|
||||
}
|
||||
|
||||
public static function getTypes(): array
|
||||
{
|
||||
return ['sections' => 1, 'producers' => 1];
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return translate($this->title, 'sections');
|
||||
}
|
||||
|
||||
public function isProducer(): bool
|
||||
{
|
||||
return $this->getWindow()->getName() === 'producers';
|
||||
}
|
||||
|
||||
public function isSection(): bool
|
||||
{
|
||||
return $this->getWindow()->getName() === 'sections';
|
||||
}
|
||||
|
||||
private function getInherit(): string
|
||||
{
|
||||
if (isset($this->inherit)) {
|
||||
return $this->inherit;
|
||||
}
|
||||
|
||||
$key = FilterUtil::INHERIT_FILTER_SETTINGS_KEY;
|
||||
|
||||
$query = sqlQueryBuilder()
|
||||
->select("COALESCE(JSON_EXTRACT(data, '$.{$key}'), 'N') AS inherit")
|
||||
->andWhere(Operator::equals(['id' => $this->getID()]));
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$query->from('producers');
|
||||
} else {
|
||||
$query->from('sections');
|
||||
}
|
||||
|
||||
if ($result = $query->execute()->fetchOne()) {
|
||||
return $this->inherit = json_decode($result) ?? 'N';
|
||||
}
|
||||
|
||||
return $this->inherit = 'N';
|
||||
}
|
||||
|
||||
private function saveInherit(string $inherit): void
|
||||
{
|
||||
$key = FilterUtil::INHERIT_FILTER_SETTINGS_KEY;
|
||||
|
||||
$query = sqlQueryBuilder()
|
||||
->set('data', "JSON_SET(data, '$.{$key}', :inherit)")
|
||||
->andWhere(Operator::equals(['id' => $this->getID()]))
|
||||
->setParameter('inherit', $inherit);
|
||||
|
||||
if ($this->isProducer()) {
|
||||
$query->update('producers');
|
||||
} else {
|
||||
$query->update('sections');
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
20
bundles/KupShop/CatalogBundle/Admin/Tabs/ProducersGPSR.php
Normal file
20
bundles/KupShop/CatalogBundle/Admin/Tabs/ProducersGPSR.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
|
||||
class ProducersGPSR extends WindowTab
|
||||
{
|
||||
use \DatabaseCommunication;
|
||||
|
||||
protected $title = 'flapGPSR';
|
||||
protected $template = 'window/producersGPSR.tpl';
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'producers' => 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
use KupShop\AdminBundle\AdminBlocksTrait;
|
||||
use KupShop\AdminBundle\Util\BlocksHistory;
|
||||
use KupShop\ContentBundle\Util\BlocksTrait;
|
||||
|
||||
class ProductsDescriptionPlus extends WindowTab
|
||||
{
|
||||
use \DatabaseCommunication;
|
||||
use BlocksTrait;
|
||||
use AdminBlocksTrait;
|
||||
|
||||
protected $title = 'flapBlocks';
|
||||
|
||||
protected $template = 'window/products.description.plus.tpl';
|
||||
|
||||
protected BlocksHistory $blocksHistory;
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'products' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public static function isAllowed()
|
||||
{
|
||||
return findModule(\Modules::PRODUCTS, \Modules::SUB_DESCR_PLUS);
|
||||
}
|
||||
|
||||
public function getVars($smarty_tpl_vars)
|
||||
{
|
||||
$blockID = $this->selectSQL('products', ['id' => $this->getID()], ['id_block'])->fetchColumn();
|
||||
if ($blockID) {
|
||||
$blocks = $this->getBlocks((int) $blockID);
|
||||
$history = $this->blocksHistory->getBlocksHistory($blockID);
|
||||
} else {
|
||||
$blocks = [];
|
||||
$history = [];
|
||||
}
|
||||
|
||||
$photos = sqlQueryBuilder()->select('id_photo')->from('photos_products_descr_plus_relation')
|
||||
->where(\Query\Operator::equals(['id_product' => $this->getID()]))
|
||||
->orderBy('position')->execute()->fetchAll();
|
||||
|
||||
return [
|
||||
'blocks' => $blocks,
|
||||
'photos' => $photos,
|
||||
'blocks_history' => $history,
|
||||
];
|
||||
}
|
||||
|
||||
public function handleCopyDescription()
|
||||
{
|
||||
if ($productID = getVal('copy_id')) {
|
||||
$copyRootBlockID = $this->selectSQL('products', ['id' => $productID], ['id_block'])->fetchColumn();
|
||||
|
||||
if ($copyRootBlockID) {
|
||||
$blockID = $this->duplicateBlock($copyRootBlockID);
|
||||
|
||||
$this->updateSQL('products', ['id_block' => $blockID], ['id' => $this->getID()]);
|
||||
|
||||
$data = [
|
||||
'id' => $this->getID(),
|
||||
'copy_id' => $productID,
|
||||
'count' => returnSQLResult('SELECT COUNT(*) FROM photos_products_descr_plus_relation WHERE id_product=:field', ['field' => $this->getID()]),
|
||||
];
|
||||
|
||||
sqlQuery(
|
||||
"REPLACE INTO photos_products_descr_plus_relation
|
||||
(id_photo, id_product, date_added, show_in_lead, position)
|
||||
SELECT id_photo, :id, NOW(), 'N', position+:count
|
||||
FROM photos_products_descr_plus_relation WHERE id_product = :copy_id",
|
||||
$data
|
||||
);
|
||||
|
||||
\Photos::checkLeadPhoto('photos_products_descr_plus_relation', 'id_product', $this->getID());
|
||||
}
|
||||
}
|
||||
|
||||
$this->window->returnOK();
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
if ($this->getAction() == 'edit') {
|
||||
// historii je treba ulozit jeste pred ulozenim bloku
|
||||
$this->blocksHistory->saveBlocksHistory(getVal('blocks', $this->getGlobalData(), []));
|
||||
}
|
||||
$this->saveBlocks($this->getGlobalData(), $this->getID(), 'products');
|
||||
}
|
||||
|
||||
public function getGlobalData()
|
||||
{
|
||||
return getVal('data', null, []);
|
||||
}
|
||||
|
||||
public function getLabel()
|
||||
{
|
||||
return translate($this->title, 'products');
|
||||
}
|
||||
|
||||
public function returnError($ErrStr, $parentRefresh = '', $ID = null)
|
||||
{
|
||||
$this->window->returnError($ErrStr, $parentRefresh, $ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setBlocksHistory(BlocksHistory $blocksHistory): void
|
||||
{
|
||||
$this->blocksHistory = $blocksHistory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
use Query\Operator;
|
||||
|
||||
class ProductsDescriptionPlusImages extends WindowTab
|
||||
{
|
||||
use \DatabaseCommunication;
|
||||
|
||||
protected $title = 'flapDescriptionPlusPhotos';
|
||||
protected $template = 'window/products.description.plus.photos.tpl';
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'products' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public static function isAllowed()
|
||||
{
|
||||
return findModule(\Modules::PRODUCTS, \Modules::SUB_DESCR_PLUS);
|
||||
}
|
||||
|
||||
public function isVisible()
|
||||
{
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('COUNT(*)')
|
||||
->from('photos_products_descr_plus_relation')
|
||||
->where(Operator::equals(['id_product' => getVal('ID')]))
|
||||
->execute();
|
||||
if (empty($qb->fetchColumn())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getAction() != 'add';
|
||||
}
|
||||
|
||||
public function getLabel()
|
||||
{
|
||||
return translate($this->title, 'products');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\ProductsFilter;
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
|
||||
class VirtualCategorySettings extends WindowTab
|
||||
{
|
||||
protected $title = 'flapVirtualCategory';
|
||||
|
||||
protected $template = 'window/sections.virtual.tpl';
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'sections' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public function isVisible()
|
||||
{
|
||||
return ($this->getAction() !== 'remove') && (getVal('ID') > 0);
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
$virtual = getVal('data')['virtual'] ?? null;
|
||||
if (getVal('Submit') && ($virtual == 'Y')) {
|
||||
$customData = $this->window->getCustomData();
|
||||
$filter = getVal('filter');
|
||||
$customData['virtual_settings'] = ProductsFilter::cleanFilter($filter);
|
||||
$this->window->setCustomData($customData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return translate($this->title, 'sections');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
$txt_str['parameterValues'] = [
|
||||
'titleEdit' => 'Editace hodnoty parametru',
|
||||
'titleAdd' => 'Přidat hodnotu parametru',
|
||||
'flapParameterValue' => 'Hodnota parametru',
|
||||
|
||||
'id' => 'ID',
|
||||
'actions' => 'Akce',
|
||||
'value' => 'Hodnota',
|
||||
'description' => 'Popis',
|
||||
'position' => 'Pozice',
|
||||
'filterUrl' => 'URL filtru',
|
||||
'title' => 'Nadpis',
|
||||
];
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
$txt_str['producersGPSR'] = [
|
||||
'flapGPSR' => 'GPSR kontakt',
|
||||
'GPSR' => 'GPSR',
|
||||
|
||||
'company_name' => 'Název společnosti',
|
||||
'company_street' => 'Ulice',
|
||||
'company_city' => 'Město',
|
||||
'company_zip' => 'PSČ',
|
||||
'company_country' => 'Země',
|
||||
'company_email' => 'E-mail',
|
||||
'company_phone' => 'Telefon',
|
||||
'company_web' => 'Web kontaktní',
|
||||
|
||||
'producer' => 'Výrobce',
|
||||
'importer' => 'Dovozce do EU',
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
$txt_str['producersGPSRList'] = [
|
||||
'company_name' => 'V: Název společnosti',
|
||||
'company_street' => 'V: Ulice',
|
||||
'company_city' => 'V: Město',
|
||||
'company_zip' => 'V: PSČ',
|
||||
'company_country' => 'V: Země',
|
||||
'company_email' => 'V: E-mail',
|
||||
'company_phone' => 'V: Telefon',
|
||||
'company_web' => 'V: Web',
|
||||
|
||||
'import_company_name' => 'D: Název společnosti',
|
||||
'import_company_street' => 'D: Ulice',
|
||||
'import_company_city' => 'D: Město',
|
||||
'import_company_zip' => 'D: PSČ',
|
||||
'import_company_country' => 'D: Země',
|
||||
'import_company_email' => 'D: E-mail',
|
||||
'import_company_phone' => 'D: Telefon',
|
||||
'import_company_web' => 'D: Web',
|
||||
];
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
$txt_str['parameterValues'] = [
|
||||
'titleEdit' => 'Edit parameter value',
|
||||
'titleAdd' => 'Add parameter value',
|
||||
'flapParameterValue' => 'Parameter value',
|
||||
|
||||
'id' => 'ID',
|
||||
'actions' => 'Actions',
|
||||
'value' => 'Value',
|
||||
'description' => 'Description',
|
||||
'position' => 'Position',
|
||||
'filterUrl' => 'Filter URL',
|
||||
'title' => 'Title',
|
||||
];
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
$txt_str['producersGPSR'] = [
|
||||
'flapGPSR' => 'GPSR contact',
|
||||
|
||||
'company_name' => 'Company name',
|
||||
'company_street' => 'Street address',
|
||||
'company_city' => 'City',
|
||||
'company_zip' => 'ZIP code',
|
||||
'company_country' => 'Country',
|
||||
'company_email' => 'Email',
|
||||
'company_phone' => 'Phone',
|
||||
'company_web' => 'Web',
|
||||
|
||||
'producer' => 'Producer',
|
||||
'importer' => 'Importer to EU',
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
$txt_str['producersGPSRList'] = [
|
||||
'company_name' => 'P: Company name',
|
||||
'company_street' => 'P: Street',
|
||||
'company_city' => 'P: City',
|
||||
'company_zip' => 'P: ZIP',
|
||||
'company_country' => 'P: Country',
|
||||
'company_email' => 'P: Email',
|
||||
'company_phone' => 'P: Phone',
|
||||
'company_web' => 'P: Web',
|
||||
|
||||
'import_company_name' => 'I: Company name',
|
||||
'import_company_street' => 'I: Street',
|
||||
'import_company_city' => 'I: City',
|
||||
'import_company_zip' => 'I: ZIP',
|
||||
'import_company_country' => 'I: Country',
|
||||
'import_company_email' => 'I: Email',
|
||||
'import_company_phone' => 'I: Phone',
|
||||
'import_company_web' => 'I: Web',
|
||||
];
|
||||
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\lists;
|
||||
|
||||
use KupShop\AdminBundle\AdminList\BaseList;
|
||||
use KupShop\CatalogBundle\Query\Search;
|
||||
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
|
||||
use Query\Operator;
|
||||
use Query\QueryBuilder;
|
||||
|
||||
class ParameterValuesList extends BaseList
|
||||
{
|
||||
use \AdminListSortable;
|
||||
|
||||
protected $template = 'listSortable.tpl';
|
||||
|
||||
protected $tableDef = [
|
||||
'id' => 'pl.id',
|
||||
'fields' => [
|
||||
'position' => ['translate' => true, 'field' => 'pl.position', 'size' => 0.5, 'render' => 'renderPosition', 'fieldType' => self::TYPE_POSITION],
|
||||
'id' => ['translate' => true, 'field' => 'pl.id', 'size' => 0.5],
|
||||
'value' => ['translate' => true, 'field' => 'pl.value', 'fieldType' => self::TYPE_STRING],
|
||||
'description' => ['translate' => true, 'label' => 'getDescriptionLabel', 'field' => 'pl.description', 'fieldType' => self::TYPE_STRING, 'render' => 'renderDescription'],
|
||||
'progress' => ['translate' => true, 'translation_section' => 'parameters', 'field' => 'pl.description', 'render' => 'renderProgress'],
|
||||
'filterUrl' => ['translate' => true, 'field' => 'pl.filter_url', 'fieldType' => self::TYPE_STRING],
|
||||
'title' => ['translate' => true, 'field' => 'pl.title', 'fieldType' => self::TYPE_STRING],
|
||||
'actions' => ['translate' => true, 'field' => 'pl.id', 'render' => 'renderActions'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $tableName = 'parameters_list';
|
||||
protected ?string $tableAlias = 'pl';
|
||||
|
||||
protected $showMassEdit = true;
|
||||
|
||||
public function customizeTableDef($tableDef)
|
||||
{
|
||||
$tableDef = parent::customizeTableDef($tableDef);
|
||||
|
||||
if (!findModule(\Modules::INDEXED_FILTER)) {
|
||||
unset($tableDef['fields']['filterUrl'], $tableDef['fields']['title']);
|
||||
}
|
||||
|
||||
if ($this->getMeaning() != 'progress') {
|
||||
unset($tableDef['fields']['progress']);
|
||||
}
|
||||
|
||||
return $tableDef;
|
||||
}
|
||||
|
||||
public function getDescriptionLabel($column, $label)
|
||||
{
|
||||
$meaning = $this->getMeaning();
|
||||
|
||||
if (in_array($meaning, ['image', 'color'])) {
|
||||
return translate($meaning, 'parameters');
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
$qb = parent::getQuery();
|
||||
|
||||
$qb->addSelect('COUNT(pp.id) as cnt')
|
||||
->leftJoin('pl', 'parameters_products', 'pp', 'pp.value_list = pl.id AND pp.id_parameter = pl.id_parameter')
|
||||
->orderBy('pl.position, pl.name', 'ASC')
|
||||
->groupBy('pl.id');
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
private function modifyBySearch(QueryBuilder $qb): void
|
||||
{
|
||||
$param = getVal('searchTerm');
|
||||
if ($param) {
|
||||
$qb->andWhere(
|
||||
Search::searchFields($param, [
|
||||
['field' => 'value', 'match' => 'both'],
|
||||
['field' => 'description', 'match' => 'both'],
|
||||
], 'OR')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getFilterQuery(): QueryBuilder
|
||||
{
|
||||
$qb = parent::getFilterQuery();
|
||||
|
||||
if ($parameterId = getVal('parameterId')) {
|
||||
$qb->andWhere(Operator::equals(['id_parameter' => $parameterId]));
|
||||
}
|
||||
|
||||
$this->modifyBySearch($qb);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function renderActions(array $values): HTML
|
||||
{
|
||||
return HTML::create('div')
|
||||
->class('btn-group')
|
||||
// counts button
|
||||
->tag('a')
|
||||
->attr('href', "javascript:nw('productsList', '', 'parameter_value={$values['id']}&showOld=1')")
|
||||
->class('btn btn-sm btn-secondary')
|
||||
->text((string) ($values['cnt'] ?: '0'))
|
||||
->end()
|
||||
// merge button
|
||||
->tag('a')
|
||||
->attr('href', "javascript:nw('parameters', '', 'acn=merge&ID={$values['id_parameter']}&id_value={$values['id']}&autoclose=1')")
|
||||
->class('btn btn-sm btn-warning')
|
||||
->tag('span')
|
||||
->class('bi bi-arrow-left-right')
|
||||
->end()
|
||||
->end()
|
||||
// delete button
|
||||
->tag('a')
|
||||
->attr('href', "/admin/launch.php?s=list.php&type=parameterValues¶meterId=83&acn=deleteValue&delete={$values['id']}")
|
||||
->class('btn btn-sm btn-danger')
|
||||
->tag('span')
|
||||
->class('bi bi-trash')
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
public function renderDescription($values, $column): ?HTML
|
||||
{
|
||||
$value = $this->renderCell($values, $column);
|
||||
|
||||
switch ($this->getMeaning()) {
|
||||
case 'image':
|
||||
return HTML::create('a')
|
||||
->attr('href', $value)
|
||||
->attr('target', '_blank')
|
||||
->tag('img')
|
||||
->class('img-rounded')
|
||||
->attr('src', $value)
|
||||
->attr('style', 'width: 50px; height: 50px; object-fit: cover;')
|
||||
->end()
|
||||
->end();
|
||||
|
||||
case 'color':
|
||||
$background = "background-color: {$value}";
|
||||
$data = json_decode($values['data'] ?? '', true);
|
||||
if (($data['enable_multicolor'] ?? 'N') == 'Y') {
|
||||
$background = 'background: url(/static/images/color-multi.jpg)';
|
||||
if (count($data['colors']) == 2) {
|
||||
$background = "background: linear-gradient(135deg, {$data['colors'][1]} 50%, {$data['colors'][2]} 50%)";
|
||||
}
|
||||
}
|
||||
|
||||
return HTML::create('div')
|
||||
->attr('style', "width: 30px; height: 30px; {$background}")
|
||||
->end();
|
||||
}
|
||||
|
||||
return HTML::create('span')->text($value);
|
||||
}
|
||||
|
||||
public function renderProgress($values, $column): ?HTML
|
||||
{
|
||||
$data = json_decode($values['data'] ?? '', true);
|
||||
|
||||
if (empty($data['progress']['value']) || empty($data['progress']['max'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$html = HTML::create('div')
|
||||
->class('progress')
|
||||
->attr('style', 'width: 200px; height: 10px; display: flex; justify-content: space-between;');
|
||||
|
||||
foreach (range(1, $data['progress']['max']) as $item) {
|
||||
$color = ($item <= $data['progress']['value']) ? '#AAAAAA' : '#DDDDDD';
|
||||
$html->tag('div')
|
||||
->attr('style', "width: 100%; height: 10px; margin: 0 1px; border-radius: 3px; background-color: {$color}")
|
||||
->end();
|
||||
}
|
||||
|
||||
return $html->end();
|
||||
}
|
||||
|
||||
public function handleDrag(): void
|
||||
{
|
||||
$url = $_POST['url'] ?? null;
|
||||
$parsed = [];
|
||||
if ($url) {
|
||||
parse_str($url, $parsed);
|
||||
}
|
||||
|
||||
$extraWhere = '';
|
||||
if ($parameterId = getVal('parameterId', $parsed)) {
|
||||
$extraWhere = " AND id_parameter = {$parameterId}";
|
||||
}
|
||||
|
||||
$this->saveList(getVal('moved_item'), 'parameters_list', ['position', 'value'], $extraWhere);
|
||||
exit(json_encode(['result' => true]));
|
||||
}
|
||||
|
||||
public function handleDeleteValue(): void
|
||||
{
|
||||
if ($valueId = getVal('delete')) {
|
||||
sqlQueryBuilder()
|
||||
->delete('parameters_list')
|
||||
->where(Operator::equals(['id' => $valueId]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
redirect($_SERVER['HTTP_REFERER']);
|
||||
}
|
||||
|
||||
public function getMeaning()
|
||||
{
|
||||
if (!$parameterId = getVal('parameterId')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->meaning)) {
|
||||
return $this->meaning;
|
||||
}
|
||||
|
||||
return $this->meaning = sqlQueryBuilder()
|
||||
->select('value_meaning')
|
||||
->from('parameters')
|
||||
->where(Operator::equals(['id' => $parameterId]))
|
||||
->execute()->fetchOne();
|
||||
}
|
||||
}
|
||||
|
||||
return ParameterValuesList::class;
|
||||
624
bundles/KupShop/CatalogBundle/Admin/lists/StockInMissingList.php
Normal file
624
bundles/KupShop/CatalogBundle/Admin/lists/StockInMissingList.php
Normal file
@@ -0,0 +1,624 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin\lists;
|
||||
|
||||
use KupShop\AdminBundle\AdminList\BaseList;
|
||||
use KupShop\AdminBundle\Util\AdminClassLocator;
|
||||
use KupShop\AdminBundle\Util\ProductListFilter;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
|
||||
use Query\Operator;
|
||||
use Query\QueryBuilder;
|
||||
|
||||
class StockInMissingList extends BaseList
|
||||
{
|
||||
protected $template = 'list/stockInMissing.tpl';
|
||||
|
||||
protected $orderParam = [
|
||||
'sort' => 'Produkt',
|
||||
'direction' => 'ASC',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tableDef = [
|
||||
'id' => 'p.id',
|
||||
'class' => 'getRowClass',
|
||||
'fields' => [
|
||||
'product' => ['field' => 'ptitle', 'size' => 2, 'render' => 'renderProduct', 'translate' => true, 'params' => 'refresh=noopener'],
|
||||
'in_store' => ['field' => 'in_store', 'render' => 'renderInStore', 'size' => 0.4, 'translate' => true],
|
||||
'suppliers_name' => ['field' => '',
|
||||
'translate' => true,
|
||||
'render' => 'renderSuppliersName',
|
||||
'class' => '',
|
||||
'size' => 1,
|
||||
],
|
||||
'suppliers_code' => ['field' => '', 'render' => 'renderSuppliersCode', 'size' => 1, 'translate' => true],
|
||||
'suppliers_price' => ['field' => '',
|
||||
'translate' => true,
|
||||
'render' => 'renderSuppliersPrice',
|
||||
'class' => 'text-right alignRight',
|
||||
'size' => 0.6,
|
||||
],
|
||||
'suppliers_in_store' => ['field' => '',
|
||||
'translate' => true,
|
||||
'render' => 'renderSuppliersInStore',
|
||||
'class' => 'text-right alignRight',
|
||||
'size' => 0.4,
|
||||
],
|
||||
'suppliers_buy' => ['field' => '',
|
||||
'translate' => true,
|
||||
'render' => 'renderSuppliersBuy',
|
||||
'class' => '',
|
||||
'size' => 1,
|
||||
],
|
||||
'suppliers_ordered' => ['field' => 'pieces_in_order_list',
|
||||
'translate' => true,
|
||||
'render' => 'renderPiecesOrdered',
|
||||
'tooltip' => 'inOrderTooltip',
|
||||
'size' => 0.4, 'class' => 'left tiny-list-items align-td',
|
||||
],
|
||||
'in_store_min' => ['field' => 'in_store_min', 'size' => 0.7, 'visible' => 'N', 'translate' => true],
|
||||
'interval_sold' => [
|
||||
'field' => 'sold',
|
||||
'size' => 0.5,
|
||||
'visible' => 'N',
|
||||
'translate' => fn ($key, $translation_section) => sprintf(translate($key, $translation_section), intval(getVal('sellInterval', null, 30))),
|
||||
],
|
||||
'interval_store' => [
|
||||
'field' => 'remaining_predict',
|
||||
'render' => 'renderRemaining',
|
||||
'size' => 0.5,
|
||||
'visible' => 'N',
|
||||
'translate' => fn ($key, $translation_section) => sprintf(translate($key, $translation_section), intval(getVal('interval', null, 14))),
|
||||
],
|
||||
'days_remaining' => [
|
||||
'field' => 'days',
|
||||
'render' => 'renderRound',
|
||||
'visible' => 'N',
|
||||
'size' => 0.5,
|
||||
'translate' => true,
|
||||
],
|
||||
'lost' => [
|
||||
'translate' => getVal('search') == 'missing' ? 'lostMoneyNotOrder' : 'lostMoney',
|
||||
'field' => 'lost',
|
||||
'render' => 'renderPrice',
|
||||
'visible' => 'N',
|
||||
],
|
||||
'sell_price' => ['field' => 'price', 'render' => 'renderPriceFinal', 'translate' => true, 'class' => 'text-right alignRight', 'visible' => 'N'],
|
||||
'future_pieces' => ['field' => 'futurePieces', 'size' => 0.4, 'tooltip' => 'towardsTooltip', 'translate' => true, 'visible' => 'N'],
|
||||
'preorder_pieces' => ['field' => 'preorderPieces', 'size' => 0.4, 'tooltip' => 'towardsTooltip', 'translate' => true, 'visible' => 'N'],
|
||||
'ean' => ['field' => 'ean', 'size' => 1, 'visible' => 'N', 'translate' => true],
|
||||
'image' => ['field' => 'id_photo', 'render' => 'renderImage', 'size' => 0.5, 'visible' => 'N', 'translate' => true],
|
||||
'margin' => ['field' => '', 'translate' => true, 'visible' => 'N', 'render' => 'renderMargin'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected $tableDef = [];
|
||||
|
||||
public function customizeTableDef($tableDef)
|
||||
{
|
||||
$tableDef = parent::customizeTableDef($tableDef);
|
||||
|
||||
if (!findModule(\Modules::ORDERS_OF_SUPPLIERS)) {
|
||||
unset($tableDef['fields']['suppliers_ordered']);
|
||||
$tableDef['fields']['suppliers_buy']['size'] = 0.5;
|
||||
}
|
||||
|
||||
$tableDef['fields']['image']['spec'] = function (QueryBuilder $qb) {
|
||||
$qb->addSelect('ppr.id_photo as id_image, ph.image_2, ph.source as image_source')
|
||||
->leftJoin('sq', 'photos_products_relation', 'ppr', 'ppr.id_product = sq.id AND ppr.show_in_lead = "Y"')
|
||||
->leftJoin('ppr', 'photos', 'ph', 'ph.id = ppr.id_photo');
|
||||
};
|
||||
|
||||
$tableDef['fields']['sell_price']['spec'] = function (QueryBuilder $qb) {
|
||||
$qb->addSelect('sq.price, sq.vat, sq.discount');
|
||||
};
|
||||
|
||||
if (findModule(\Modules::PRODUCTS, \Modules::SUB_NOTE)) {
|
||||
$tableDef['fields']['product_note'] = [
|
||||
'field' => 'pnote',
|
||||
'translate' => true,
|
||||
'visible' => 'N',
|
||||
];
|
||||
}
|
||||
|
||||
if (findModule(\Modules::PRODUCTS_BATCHES)) {
|
||||
$tableDef['fields']['batch'] = ['field' => 'batch', 'size' => 0.5, 'render' => 'renderBoolean', 'visible' => 'N', 'translate' => true];
|
||||
}
|
||||
|
||||
return $tableDef;
|
||||
}
|
||||
|
||||
public function getRowClass($values)
|
||||
{
|
||||
$class = 'no-row-click';
|
||||
if (0 <= $values['pieces_with_orders']) {
|
||||
$class .= ' row-green';
|
||||
} elseif ($values['in_store'] < 0) {
|
||||
$class .= ' row-orange';
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public function renderRound($values, $column)
|
||||
{
|
||||
$lost = $this->getListRowValue($values, $column['field']);
|
||||
|
||||
if (!isset($lost)) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return round($lost, 1);
|
||||
}
|
||||
|
||||
public function renderRemaining($values, $column)
|
||||
{
|
||||
$lost = $this->getListRowValue($values, $column['field']);
|
||||
|
||||
$futurePieces = $values['futurePieces'] ?? 0;
|
||||
|
||||
return round($lost + $futurePieces, 1);
|
||||
}
|
||||
|
||||
public function renderInStore($values, $column)
|
||||
{
|
||||
$return = HTML::create('p')
|
||||
->tag('span')
|
||||
->class('badge')
|
||||
->text($values['in_store'])
|
||||
->end();
|
||||
|
||||
if (!empty($values['futurePieces'])) {
|
||||
$return->tag('span')
|
||||
->class('badge badge-warning')
|
||||
->attr('title', 'V budoucí faktuře')
|
||||
->text($values['futurePieces'])
|
||||
->end();
|
||||
}
|
||||
|
||||
if (!empty($values['preorderPieces'])) {
|
||||
$return->tag('span')
|
||||
->class('badge badge-danger')
|
||||
->attr('title', 'V předobjednávkové faktuře')
|
||||
->text($values['preorderPieces'])
|
||||
->end();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function renderMargin(array $values, array $column)
|
||||
{
|
||||
return $this->renderPerSupplierColumn($values, function ($output, $data) {
|
||||
$output->text(toDecimal($data['margin'])->round()->asFloat().'%');
|
||||
});
|
||||
}
|
||||
|
||||
public function renderImage($values, $column)
|
||||
{
|
||||
$img = getImage($values['id_image'], $values['image_2'], $values['image_source'], 0);
|
||||
|
||||
if (empty($img)) {
|
||||
$path = '/admin/static/images/no-image.png';
|
||||
} else {
|
||||
$path = $this->getUrlFinder()->staticUrl($img['source']);
|
||||
}
|
||||
|
||||
return HTML::create('a')
|
||||
->attr('href', $path)
|
||||
->attr('target', '_blank')
|
||||
->class('btn btn-xs btn-secondary')
|
||||
->tag('i')
|
||||
->class('bi bi-zoom-in')
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
protected function renderPerSupplierColumn(array $values, callable $callback)
|
||||
{
|
||||
$suppliers = $this->getProductSuppliers($values['id'], $values['id_variation']);
|
||||
|
||||
$output = HTML::create('div')->tag('table')->class('table-orders-of-suppliers');
|
||||
foreach ($suppliers as $i => $supplier) {
|
||||
$td = $output->tag('tr')
|
||||
->tag('td');
|
||||
$callback($td, $supplier, $i);
|
||||
$output->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
return $output->end();
|
||||
}
|
||||
|
||||
protected function getProductSuppliers(int $id_product, ?int $id_variation): array
|
||||
{
|
||||
$id = $id_product.'/'.$id_variation;
|
||||
|
||||
if (!isset($this->suppliers[$id])) {
|
||||
$qb = sqlQueryBuilder()->select('pos.id pos_id, s.name, pos.in_store, s.id as idSupp, pos.code, s.order_url, p.vat')
|
||||
->from('suppliers', 's')
|
||||
->leftJoin('s', 'products_of_suppliers', 'pos', 'pos.id_supplier = s.id')
|
||||
->leftJoin('pos', 'products', 'p', 'p.id = pos.id_product')
|
||||
->leftJoin('p', 'products_variations', 'pv', 'p.id = pv.id_product')
|
||||
->andWhere(Operator::andX(
|
||||
Operator::equals(['pos.id_product' => $id_product]),
|
||||
Operator::equalsNullable(['pos.id_variation' => $id_variation]),
|
||||
))
|
||||
->orderBy('s.id, pos.id_product, pos.id_variation')
|
||||
->groupBy('s.id');
|
||||
|
||||
if (findModule(\Modules::ORDERS_OF_SUPPLIERS)) {
|
||||
$qb->leftJoin('pos', 'orders_of_suppliers', 'oos',
|
||||
'pos.id_product = oos.id_product AND pos.id_supplier = oos.id_supplier AND
|
||||
pos.id_variation<=>oos.id_variation')
|
||||
->addSelect('COALESCE(oos.pieces, 0) pieces_in_order');
|
||||
} else {
|
||||
$qb->addSelect('0 pieces_in_order');
|
||||
}
|
||||
|
||||
$subPriceModule = findModule(\Modules::PRODUCTS, \Modules::SUB_PRICE_BUY);
|
||||
$qb->addSelect(Operator::coalesce(
|
||||
'pos.price_buy',
|
||||
$subPriceModule && findModule(\Modules::PRODUCTS_VARIATIONS) ? 'pv.price_buy' : null,
|
||||
$subPriceModule ? 'p.price_buy' : null,
|
||||
0
|
||||
).' price_buy');
|
||||
|
||||
$coalesce = '0';
|
||||
if ($subPriceModule) {
|
||||
$coalesce = findModule(\Modules::PRODUCTS_VARIATIONS) ? 'pv.price_buy, p.price_buy' : 'p.price_buy';
|
||||
}
|
||||
|
||||
$qb->addSelect('100 - ((COALESCE(pos.price_buy, '.$coalesce.', 0) / COALESCE(pv.price, p.price)) * 100) as margin');
|
||||
|
||||
$this->suppliers[$id] = $qb->execute()->fetchAll();
|
||||
}
|
||||
|
||||
return $this->suppliers[$id];
|
||||
}
|
||||
|
||||
public function renderSuppliersName($values, $column)
|
||||
{
|
||||
$suppliers = $this->getProductSuppliers($values['id'], $values['id_variation']);
|
||||
|
||||
if (empty($suppliers)) {
|
||||
return HTML::create('a')
|
||||
->attr('title', 'Přidat dodavatele')
|
||||
->attr('href', "javascript:nw('productsOfSuppliers', '0', 'data[id_product]={$values['id']}&data[id_variation]={$values['id_variation']}&autoclose=1');")
|
||||
->tag('i')
|
||||
->class('bi bi-plus-lg list-yes')
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
return $this->renderPerSupplierColumn($values, function ($output, $data) {
|
||||
$output->tag('a')
|
||||
->attr('href', "javascript:nw('productsOfSuppliers', {$data['pos_id']})")
|
||||
->text($data['name'])
|
||||
->attr('title', $data['name'])
|
||||
->end();
|
||||
});
|
||||
}
|
||||
|
||||
public function renderSuppliersInStore($values, $column)
|
||||
{
|
||||
$pieces_ordered = explode(',', $values['pieces_in_order_list']);
|
||||
|
||||
return $this->renderPerSupplierColumn($values, function ($output, $data, $i) use ($pieces_ordered) {
|
||||
if (!is_null($data['in_store'])) {
|
||||
$supplierAmount = max(($data['in_store'] ?? 0) - $pieces_ordered[$i], 0);
|
||||
$output->text($supplierAmount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function renderSuppliersBuy($values, $column)
|
||||
{
|
||||
$search = getVal('search');
|
||||
$orderAmount = max(0, -(($search == 'missing') ? $values['pieces_with_orders_predict'] : $values['pieces_with_orders']));
|
||||
|
||||
return $this->renderPerSupplierColumn($values, function ($output, $data) use ($values, $orderAmount) {
|
||||
if (!is_null($data['in_store'])) {
|
||||
$orderAmount = min(ceil($orderAmount), $data['in_store']);
|
||||
}
|
||||
|
||||
if (findModule(\Modules::ORDERS_OF_SUPPLIERS)) {
|
||||
$output->attr('style', 'width50%;')
|
||||
->tag('input')
|
||||
->attr('type', 'number')
|
||||
->attr('name', 'pieces')
|
||||
->attr('placeholder', $orderAmount < 0 ? 0 : ceil($orderAmount))
|
||||
->attr('style', 'width:40px;')
|
||||
->end()
|
||||
->text(' ks')
|
||||
->end()
|
||||
->tag('td')
|
||||
->attr('style', 'width:50%;')
|
||||
->tag('a')
|
||||
->attr('data-acn', 'OrdersOfSupplierAdd')
|
||||
->attr('data-adddata', json_encode([
|
||||
'id_product' => $values['id'],
|
||||
'id_variation' => $values['id_variation'],
|
||||
'id_supplier' => $data['idSupp'],
|
||||
]))
|
||||
->class('btn btn-xs btn-primary btn-block')
|
||||
->tag('span')
|
||||
->class('bi bi-cart')
|
||||
->end()
|
||||
->end();
|
||||
} else {
|
||||
$output->tag('a')
|
||||
->attr('data-acn', 'OrdersAdd')
|
||||
->attr('data-adddata', json_encode([
|
||||
'code' => $data['code'],
|
||||
'order_url' => $data['order_url'],
|
||||
'id_supplier' => $data['idSupp'],
|
||||
]))
|
||||
->attr($data['order_url'] ? '' : 'disabled')
|
||||
->class('btn btn-xs btn-primary btn-block')
|
||||
->tag('span')
|
||||
->class('bi bi-cart')
|
||||
->end()
|
||||
->text(" {$orderAmount} ks")
|
||||
->end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function renderSuppliersPrice($values, $column)
|
||||
{
|
||||
return $this->renderPerSupplierColumn($values, fn ($output, $data) => $output->text(is_null($data['price_buy']) ? '-' :
|
||||
toDecimal($data['price_buy'])->addVat(getVat($data['vat']))->round()->asFloat().' Kč'));
|
||||
}
|
||||
|
||||
public function renderSuppliersCode($values, $column)
|
||||
{
|
||||
return $this->renderPerSupplierColumn($values, fn ($tag, $data) => $tag->text($data['code']));
|
||||
}
|
||||
|
||||
public function renderProduct($values, $column)
|
||||
{
|
||||
$html = HTML::create('div')
|
||||
->class('product-title')
|
||||
->tag('strong')
|
||||
->text($values['ptitle'])
|
||||
->end();
|
||||
|
||||
if (!empty($values['pvtitle'])) {
|
||||
$html->tag('span')
|
||||
->class('help-block')
|
||||
->text($values['pvtitle'])
|
||||
->end();
|
||||
}
|
||||
|
||||
return $html->end();
|
||||
}
|
||||
|
||||
public function renderPiecesOrdered($values, $column)
|
||||
{
|
||||
$values = $this->getListRowValue($values, $column['field']);
|
||||
|
||||
$output = HTML::create('div')->tag('table')->class('table-orders-of-suppliers');
|
||||
foreach (explode(',', $values) as $pieces_ordered) {
|
||||
$output->tag('tr')
|
||||
->tag('td')
|
||||
->text($pieces_ordered)
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
return $output->end();
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
/** @var \KupShop\ElninoBundle\\Query\QueryBuilder $qb */
|
||||
$sellIntervalOriginal = getVal('sellInterval', null, 30);
|
||||
$sellInterval = intval($sellIntervalOriginal);
|
||||
$interval = intval(getVal('interval', null, 14));
|
||||
$search = getVal('search');
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select(
|
||||
'p.id',
|
||||
'pv.id id_variation',
|
||||
'p.title ptitle',
|
||||
'pv.title pvtitle',
|
||||
'p.price pprice',
|
||||
'pv.price pvprice',
|
||||
'pos.id_supplier',
|
||||
'COALESCE(pv.in_store, p.in_store) in_store',
|
||||
'(COALESCE(pv.in_store, p.in_store, 0) - COALESCE(pv.in_store_min, p.in_store_min, 0)) as in_store_sub_min',
|
||||
'COALESCE(pv.in_store_min, p.in_store_min) as in_store_min',
|
||||
'COALESCE(pv.ean, p.ean) as ean',
|
||||
'COALESCE(pv.price, p.price) as price',
|
||||
'p.discount',
|
||||
'p.vat',
|
||||
'p.producer',
|
||||
'pos.in_store in_store_supplier',
|
||||
'pos.code code_supplier',
|
||||
)
|
||||
->from('products', 'p')
|
||||
->leftJoin('p', 'products_variations', 'pv', 'p.id = pv.id_product')
|
||||
->leftJoin('p', 'products_of_suppliers', 'pos',
|
||||
'p.id = pos.id_product AND pv.id<=>pos.id_variation')
|
||||
->groupBy('p.id, pv.id, pos.id_supplier')
|
||||
->setParameter('interval', $interval)
|
||||
->setParameter('sellInterval', $sellInterval);
|
||||
|
||||
if (findModule(\Modules::SUB_NOTE)) {
|
||||
$qb->addSelect('COALESCE(pv.note, p.note) as pnote');
|
||||
}
|
||||
|
||||
if (findModule(\Modules::ORDERS_OF_SUPPLIERS)) {
|
||||
$qb->leftJoin('pos', 'orders_of_suppliers', 'oos',
|
||||
'pos.id_product = oos.id_product AND pos.id_supplier = oos.id_supplier AND
|
||||
pos.id_variation<=>oos.id_variation')
|
||||
->addSelect('COALESCE(oos.pieces, 0) pieces_in_order');
|
||||
} else {
|
||||
$qb->addSelect('0 pieces_in_order');
|
||||
}
|
||||
|
||||
$postData = getVal('data', $_POST);
|
||||
if (!empty($postData['id_product'])) {
|
||||
$this->addOrdersOfSupplier($postData);
|
||||
$qb->andWhere(Operator::equalsNullable(['p.id' => $postData['id_product'], 'pv.id' => $postData['id_variation'] ?: null]));
|
||||
|
||||
return $this->applyTopQuery($qb);
|
||||
}
|
||||
|
||||
$notHandled = getVal('notHandled', null, false);
|
||||
$active = getVal('active', null, getVal('fromNav'));
|
||||
|
||||
if ($notHandled) {
|
||||
$existsNotHandled = sqlQueryBuilder()->select('oSub1.id')
|
||||
->from('order_items', 'oiSub1')
|
||||
->leftJoin('oiSub1', 'orders', 'oSub1', 'oSub1.id = oiSub1.id_order')
|
||||
->andWhere('oiSub1.id_product = p.id AND oiSub1.id_variation <=> pv.id')
|
||||
->andWhere(Operator::inIntArray(getStatuses('nothandled'), 'oSub1.status'))
|
||||
->groupBy('oiSub1.id_product, oiSub1.id_variation');
|
||||
|
||||
$qb->andWhere('EXISTS ('.$existsNotHandled->getSQL().')')
|
||||
->addParameters($existsNotHandled->getParameters(), $existsNotHandled->getParameterTypes());
|
||||
}
|
||||
|
||||
$ignoreMin = getVal('ignoreMin');
|
||||
|
||||
if (!($search == 'iddle' || $search == 'missing') && !$ignoreMin) {
|
||||
$qb->andWhere('COALESCE(pv.in_store, p.in_store) < COALESCE(pv.in_store_min, p.in_store_min, 0)');
|
||||
}
|
||||
|
||||
if ($active) {
|
||||
$qb->andWhere('p.figure="Y"');
|
||||
}
|
||||
|
||||
if (findModule(\Modules::PRODUCTS_BATCHES)) {
|
||||
$qb->leftJoin('p', 'products_batches', 'pb', 'pb.id_product = p.id AND pb.id_variation <=> pv.id')
|
||||
->addSelect('count(pb.code) > 0 as batch');
|
||||
}
|
||||
|
||||
// Apply product filter
|
||||
$filter = getVal('filter', null, []);
|
||||
if (!empty($filter)) {
|
||||
// extended search
|
||||
ServiceContainer::getService(ProductListFilter::class)->applyFilter($filter, $qb);
|
||||
}
|
||||
|
||||
return $this->applyTopQuery($qb);
|
||||
}
|
||||
|
||||
public function applyTopQuery($oldqb)
|
||||
{
|
||||
$futureSoldAmount = '((COALESCE(pieces_sold.sold, 0) / :sellInterval) * :interval)';
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select(
|
||||
'sq.*',
|
||||
"(COALESCE(sq.in_store_sub_min, 0) - {$futureSoldAmount}) as remaining_predict",
|
||||
'sq.in_store_sub_min / (COALESCE(pieces_sold.sold, 0) / :sellInterval) as days',
|
||||
"ROUND(((LEAST(0, sq.in_store_sub_min) - (sq.in_store_sub_min - {$futureSoldAmount})) * COALESCE(sq.pvprice, sq.pprice)), 4) as lost",
|
||||
'(sq.in_store_sub_min + SUM(COALESCE(sq.pieces_in_order, 0)) + COALESCE(sfp.quantity, 0)) as pieces_with_orders',
|
||||
"(sq.in_store_sub_min + SUM(COALESCE(sq.pieces_in_order, 0)) + COALESCE(sfp.quantity, 0) - {$futureSoldAmount}) as pieces_with_orders_predict",
|
||||
'GROUP_CONCAT(COALESCE(sq.pieces_in_order, 0)) pieces_in_order_list',
|
||||
'SUM(COALESCE(sq.pieces_in_order, 0)) pieces_in_orders',
|
||||
'SUM(COALESCE(sq.in_store_supplier, 0)) in_store_suppliers',
|
||||
)
|
||||
->from('('.$oldqb->getSQL().')', 'sq')
|
||||
->groupBy('sq.id, sq.id_variation')
|
||||
->addParameters($oldqb->getParameters(), $oldqb->getParameterTypes());
|
||||
|
||||
$subQuerySold = sqlQueryBuilder()->select('oi.id_product, oi.id_variation, SUM(oi.pieces) sold')
|
||||
->from('orders', 'o')
|
||||
->innerJoin('o', 'order_items', 'oi', 'o.id = oi.id_order')
|
||||
->setForceIndexForJoin('oi', 'id_invoice')
|
||||
->where('o.date_created > (NOW() - INTERVAL :sellInterval DAY) AND o.status_storno = 0')
|
||||
->groupBy('oi.id_product, oi.id_variation');
|
||||
|
||||
$qb->leftJoinSubQuery('sq',
|
||||
$subQuerySold,
|
||||
'pieces_sold',
|
||||
'(sq.id = pieces_sold.id_product AND sq.id_variation <=> pieces_sold.id_variation)');
|
||||
|
||||
$qb->addSelect('COALESCE(pieces_sold.sold, 0) sold');
|
||||
|
||||
// subquery Na cestě
|
||||
$subqueryFuturePieces = sqlQueryBuilder()->select('sii.id_product, sii.id_variation, IFNULL(SUM(sii.quantity), 0) quantity')
|
||||
->from('stock_in', 'si')
|
||||
->leftJoin('si', 'stock_in_items', 'sii', 'sii.id_stock_in = si.id')
|
||||
->where("si.id_index = 'future'")
|
||||
->groupBy('sii.id_product, sii.id_variation');
|
||||
|
||||
$qb->leftJoinSubQuery('sq', $subqueryFuturePieces, 'sfp', '(sq.id = sfp.id_product AND sq.id_variation <=> sfp.id_variation)');
|
||||
|
||||
// subquery Předobjednávka
|
||||
$subqueryPreordersPieces = sqlQueryBuilder()->select('sii.id_product, sii.id_variation, IFNULL(SUM(sii.quantity), 0) quantity')
|
||||
->from('stock_in', 'si')
|
||||
->leftJoin('si', 'stock_in_items', 'sii', 'sii.id_stock_in = si.id')
|
||||
->where("si.id_index = 'preorder'")
|
||||
->groupBy('sii.id_product, sii.id_variation');
|
||||
$qb->leftJoinSubQuery('sq', $subqueryPreordersPieces, 'spp', '(sq.id = spp.id_product AND sq.id_variation <=> spp.id_variation)');
|
||||
|
||||
$qb->addSelect('COALESCE(sfp.quantity,0) futurePieces', 'COALESCE(spp.quantity,0) preorderPieces');
|
||||
|
||||
// pokud posílám konkrétní IDčka, nechci už dál filtrovat
|
||||
$postData = getVal('data', $_POST);
|
||||
|
||||
if (!empty($postData['id_product'])) {
|
||||
return $qb;
|
||||
}
|
||||
|
||||
$search = getVal('search');
|
||||
$notOrdered = getVal('notOrdered', null, getVal('fromNav'));
|
||||
$buyable = getVal('buyable');
|
||||
$ignoreMin = getVal('ignoreMin');
|
||||
|
||||
if ($notOrdered && !$ignoreMin) {
|
||||
if ($search == 'missing') {
|
||||
$qb->having('pieces_with_orders_predict < 0');
|
||||
} else {
|
||||
$qb->having('pieces_with_orders < 0');
|
||||
}
|
||||
}
|
||||
|
||||
$wasSold = getVal('wasSold', null, getVal('fromNav'));
|
||||
if ($search == 'missing' && $wasSold) {
|
||||
$qb->andWhere('COALESCE(pieces_sold.sold, 0) > 0');
|
||||
}
|
||||
|
||||
if ($search == 'missing' && empty(getVal('orderFlags')) && !$ignoreMin) {
|
||||
$qb->andHaving('remaining_predict + futurePieces <= 0');
|
||||
} elseif ($search == 'iddle') {
|
||||
$qb->andHaving('remaining_predict > 0');
|
||||
}
|
||||
|
||||
if ($buyable) {
|
||||
$qb->andHaving('in_store_suppliers > 0');
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function getFinalQueryBuilder(array &$vars): QueryBuilder
|
||||
{
|
||||
$qb = parent::getFinalQueryBuilder($vars);
|
||||
$qb->addOrderBy('sq.id_supplier');
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function addOrdersOfSupplier($data)
|
||||
{
|
||||
if (!empty($data['pieces']) && findModule(\Modules::ORDERS_OF_SUPPLIERS)) {
|
||||
$adminClassLocator = ServiceContainer::getService(AdminClassLocator::class);
|
||||
$fullClassPath = $adminClassLocator->getClassPath('ordersOfSuppliers.php');
|
||||
$orders = $adminClassLocator->createClass($fullClassPath);
|
||||
$orders->setID($data['id_supplier']);
|
||||
$orders->handleAddValue($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StockInMissingList::class;
|
||||
69
bundles/KupShop/CatalogBundle/Admin/parameterValues.php
Normal file
69
bundles/KupShop/CatalogBundle/Admin/parameterValues.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\Admin;
|
||||
|
||||
use KupShop\KupShopBundle\Util\StringUtil;
|
||||
use Query\Operator;
|
||||
|
||||
class ParameterValues extends \Window
|
||||
{
|
||||
protected $tableName = 'parameters_list';
|
||||
protected $nameField = 'value';
|
||||
|
||||
public function get_vars()
|
||||
{
|
||||
$vars = parent::get_vars();
|
||||
|
||||
$parameterId = $this->getAction() === 'add' ? getVal('parameterId') : getVal('id_parameter', $vars['body']['data']);
|
||||
$vars['body']['data']['id_parameter'] = $parameterId;
|
||||
|
||||
$vars['body']['data']['value_meaning'] = sqlQueryBuilder()
|
||||
->select('value_meaning')
|
||||
->from('parameters')
|
||||
->where(Operator::equals(['id' => $parameterId]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
$this->unserializeCustomData($vars['body']['data']);
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
$data = parent::getData();
|
||||
|
||||
$colors = [];
|
||||
// kvuli ukladani dat z adminu - index 0 se v administraci nezobrazuje, proto umele navysim index, aby se barva zobrazila
|
||||
$index = 1;
|
||||
foreach ($data['data']['colors'] as $i => $item) {
|
||||
if ($i === 0 || $item['delete']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$colors[$index++] = $item['value'];
|
||||
}
|
||||
|
||||
$data['data']['colors'] = $colors;
|
||||
|
||||
if (($data['position'] ?? null) === '') {
|
||||
unset($data['position']);
|
||||
}
|
||||
|
||||
$this->serializeCustomData($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function processFormData()
|
||||
{
|
||||
$data = parent::processFormData();
|
||||
|
||||
if (($data['filter_url'] ?? false) === '') {
|
||||
$data['filter_url'] = StringUtil::slugify($data['value'] ?? '');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return ParameterValues::class;
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
class ProductsPhotos extends BaseAdminPhotos
|
||||
{
|
||||
use DatabaseCommunication;
|
||||
|
||||
protected $relation_table_name = 'photos_products_descr_plus_relation';
|
||||
protected $photo_type = 4;
|
||||
protected $tablefield = 'id_product';
|
||||
protected $photo_nametype = 'ProductDescrPlus';
|
||||
protected $copy_from = 'z produktu';
|
||||
protected $search_field = 'product_id';
|
||||
}
|
||||
|
||||
return ProductsPhotos::class;
|
||||
@@ -0,0 +1,178 @@
|
||||
{extends "[shared]window.tpl"}
|
||||
|
||||
{block tabs}
|
||||
{windowTab id='flapParameterValue'}
|
||||
{/block}
|
||||
|
||||
{block tabsContent}
|
||||
<div id="flapParameterValue" class="tab-pane fade active in boxStatic">
|
||||
<div class="wpj-main-panel-title">
|
||||
<h4>Hodnota parametru</h4>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="data[id_parameter]" value="{$body.data.id_parameter}">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-10">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'value'|translate}</label>
|
||||
<input type="text" class="form-control input-sm" name="data[value]"
|
||||
value="{$body.data.value}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'position'|translate}</label>
|
||||
<input type="text" class="form-control input-sm" name="data[position]"
|
||||
value="{$body.data.position}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{block "descriptionInput"}
|
||||
<div class="row">
|
||||
{if $body.data.value_meaning == 'progress'}
|
||||
<div class="col-xs-2">
|
||||
<div class="wpj-form-group">
|
||||
<label>
|
||||
{$body.data.value_meaning|translate:"parameters"}
|
||||
<a class="help-tip" data-toggle="tooltip"
|
||||
title="{"progress_tooltip"|translate:"parameters"}">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text" name="data[data][progress][value]" value="{$body.data.data.progress.value}" maxlength="250" class="form-control">
|
||||
<span class="input-group-addon input-group-addon-light">z</span>
|
||||
<input type="text" name="data[data][progress][max]" value="{$body.data.data.progress.max}" maxlength="250" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-xs-{if $body.data.value_meaning == 'progress'}10{else}12{/if}">
|
||||
<div class="wpj-form-group">
|
||||
<label>
|
||||
{if $body.data.value_meaning == 'text' || $body.data.value_meaning == 'progress'}
|
||||
{'description'|translate}
|
||||
{else}
|
||||
{if $body.data.value_meaning == 'color'}
|
||||
<div class="wpj-form-group d-flex align-items-center">
|
||||
{print_toggle nameRaw="data[data][enable_multicolor]" value=$body.data.data.enable_multicolor}
|
||||
<label>{'enable_multicolor'|translate:"parameters"}</label>
|
||||
</div>
|
||||
{/if}
|
||||
{$body.data.value_meaning|translate:"parameters"}
|
||||
{/if}
|
||||
</label>
|
||||
{if $body.data.value_meaning == 'image'}
|
||||
<div class="input-group">
|
||||
<input type="text" name="data[description]" value="{$body.data.description}"
|
||||
maxlength="250"
|
||||
class="form-control {if $body.data.value_meaning == 'color'}minicolors" autocomplete="off"
|
||||
data-control="hue{/if}">
|
||||
{if $body.data.value_meaning == 'image'}
|
||||
<div class="input-group-btn">
|
||||
{insert_file_browse link="attachLink"}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{elseif $body.data.value_meaning == 'color'}
|
||||
<div id="input-container">
|
||||
{if !isset($body.data.data.enable_multicolor) or $body.data.data.enable_multicolor === 'N'}
|
||||
<div class="form-group form-group-flex">
|
||||
<div class="col-xs-12">
|
||||
<input type="text" name="data[description]" value="{$body.data.description}" maxlength="250"
|
||||
class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
{else}
|
||||
<div id="colors">
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-3">
|
||||
<a href="#" data-form-add class="btn btn-success btn-block"><span
|
||||
class="glyphicon glyphicon-plus"></span> {'add_new_color'|translate:'parameters'}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-group panel-group-lists">
|
||||
{if !isset($body.data.data.colors)}
|
||||
{$body.data.data.colors = []}
|
||||
{/if}
|
||||
{foreach [[]] + $body.data.data.colors as $key => $row}
|
||||
<div class="panel" {if $key == 0}data-form-new style="display:none" {else}data-form-item{/if}>
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-8">
|
||||
<input type="text" name="data[data][colors][{$key}][value]" {if $key == 0} value="" {else} value="{$row}" {/if} maxlength="250"
|
||||
class="form-control">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<a class="btn-sm btn btn-danger" data-form-delete>
|
||||
<input class="hidden" type="checkbox" name="data[data][colors][{$key}][delete]"/>
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
<script type="application/javascript">
|
||||
initForm({
|
||||
selector: '#colors',
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
</div>
|
||||
{else}
|
||||
<div>
|
||||
<input type="text" name="data[description]" value="{$body.data.description}" maxlength="250" class="form-control">
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{ifmodule INDEXED_FILTER}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'filterUrl'|translate}</label>
|
||||
<input type="text" class="form-control input-sm" name="data[filter_url]"
|
||||
value="{$body.data.filter_url}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'title'|translate}</label>
|
||||
<input type="text" class="form-control input-sm" name="data[title]"
|
||||
value="{$body.data.title}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/ifmodule}
|
||||
|
||||
{block "custom-data"}
|
||||
{/block}
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
{block css append}
|
||||
{if $body.data.value_meaning == 'color'}
|
||||
<link rel="stylesheet" href="./static/js/jquery.minicolors.css">
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block js append}
|
||||
{if $body.data.value_meaning == 'color'}
|
||||
<script type="text/javascript" src="./static/js/jquery.minicolors.js"></script>
|
||||
{/if}
|
||||
{/block}
|
||||
|
||||
{block "js-onready" append}
|
||||
{if $body.data.value_meaning == 'color'}
|
||||
<script>
|
||||
$('.minicolors').minicolors();
|
||||
</script>
|
||||
{/if}
|
||||
{/block}
|
||||
@@ -0,0 +1,116 @@
|
||||
<div id="flapGPSR" class="tab-pane fade active in boxFlex">
|
||||
<h4>{'producer'|translate:"producersGPSR"}</h4>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_name'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="250" name="data[company_name]"
|
||||
value="{$body.data.company_name}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_street'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[company_street]"
|
||||
value="{$body.data.company_street}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_city'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[company_city]"
|
||||
value="{$body.data.company_city}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_zip'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[company_zip]"
|
||||
value="{$body.data.company_zip}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_country'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[company_country]"
|
||||
value="{$body.data.company_country}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_email'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[company_email]"
|
||||
value="{$body.data.company_email}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_web'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[company_web]"
|
||||
value="{$body.data.company_web}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>{'importer'|translate:"producersGPSR"}</h4>
|
||||
<p>Vyplňte, pokud se výrobce nenachází v EU</p>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_name'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="250" name="data[import_company_name]"
|
||||
value="{$body.data.import_company_name}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_street'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[import_company_street]"
|
||||
value="{$body.data.import_company_street}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_city'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[import_company_city]"
|
||||
value="{$body.data.import_company_city}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_zip'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[import_company_zip]"
|
||||
value="{$body.data.import_company_zip}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_country'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="50" name="data[import_company_country]"
|
||||
value="{$body.data.import_company_country}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_email'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[import_company_email]"
|
||||
value="{$body.data.import_company_email}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<div class="wpj-form-group">
|
||||
<label>{'company_web'|translate:"producersGPSR"}</label>
|
||||
<input type="text" class="form-control input-sm" maxlength="100" name="data[import_company_web]"
|
||||
value="{$body.data.import_company_web}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div id="flapDescriptionPlusPhotos" class="tab-pane fade boxFlex box iframe-box">
|
||||
<iframe class="on-demand boxFlex" src="launch.php?s=products.descr_plus.photos.php&ID={$body.data.id}" data-src="launch.php?s=products.descr_plus.photos.php&ID={$body.data.id}"></iframe>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script type="text/javascript" src="../ckeditor/ckeditor.js"></script>
|
||||
<script type="text/javascript" src="static/js/jquery.mjs.nestedSortable.min.js"></script>
|
||||
<script src="static/js/chosen.image.js"></script>
|
||||
<script type="text/javascript" src="static/js/blocks.js?2"></script>
|
||||
|
||||
|
||||
<div id="flapBlocks" class="tab-pane boxStatic box">
|
||||
<h1 class="h4 main-panel-title">
|
||||
{'flapBlocks'|translate}
|
||||
</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6 text-right">
|
||||
<input type="text" data-no-change data-autocomplete-search="products" autocomplete="off" class="form-control input-sm autocomplete-control" name="copyDescr" placeholder="{'copyDescrPlus'|translate}">
|
||||
</div>
|
||||
</div>
|
||||
{include "utils/blocks.tpl" identifiers=$cfg.Blocks.identifiers blocks=$tab.data.blocks photos=$tab.data.photos acn=$body.acn hideImgBlock=1}
|
||||
|
||||
{include "utils/blocksHistory.tpl" blocks_history=$tab.data.blocks_history}
|
||||
<script>
|
||||
$('[data-autocomplete-search=products]').adminAutoComplete({
|
||||
select: function (e, $item) {
|
||||
var item = $item.data.items[$item.item.data('autocomplete-item')];
|
||||
window.location = window.location + '&acn=copyDescription©_id=' + item.id;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@@ -0,0 +1,108 @@
|
||||
{extends 'frame.tpl'}
|
||||
|
||||
{block css append}
|
||||
<style>
|
||||
.products-filter-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.autocomplete-results img {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.panel-primary {
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
{/block}
|
||||
|
||||
{block windowContent}
|
||||
<form data-productsFilter-form>
|
||||
{include "block.productsFilter.tpl"}
|
||||
</form>
|
||||
<div class="panel-body" data-productsFilter-preview>
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<p><span data-productsFilter-count></span> vyfiltrovaných produktů</p>
|
||||
</div>
|
||||
<div class="col-md-5 pull-right">
|
||||
<button data-productsFilter-button class="btn btn-primary btn-block" onclick="transferValues(filterValue)">Vybrat produkty</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="products-filter-list row" data-productsFilter-list></div>
|
||||
</div>
|
||||
{/block}
|
||||
|
||||
<script>
|
||||
{block onready append}
|
||||
'use strict';
|
||||
|
||||
function transferValues(val) {
|
||||
var opener = window.opener;
|
||||
if (opener) {
|
||||
opener.postMessage({ selectedProducts: 'selectedProducts', filter: val }, '*');
|
||||
opener.focus();
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
var filterValue = '{$smarty.get.filter|escape:javascript nofilter}';
|
||||
|
||||
$(document).ready(function() {
|
||||
var $form = $('[data-productsFilter-form]');
|
||||
var $preview = $('[data-productsFilter-preview]');
|
||||
var $count = $('[data-productsFilter-count]');
|
||||
var $button = $('[data-productsFilter-button]');
|
||||
var $products = $('[data-productsFilter-list]');
|
||||
|
||||
var createProductMarkup = function(data) {
|
||||
return $('<div class="col-lg-3 col-md-4 col-xs-6"><div class="panel autocomplete-results"><div class="panel-body"><img src="' + data.photo +
|
||||
'" alt=""><p class="product-title"><a href="javascript:nw(\'product\', \'' + data.id + '\')">' + data.title + '</a><br><small>Kód: ' + data.code + ' | Skladem: ' +
|
||||
data.inStore + ' ks</small></p></div></div></div>');
|
||||
};
|
||||
|
||||
var xhr = null;
|
||||
|
||||
function loadProducts() {
|
||||
$button.addClass('is-submitting');
|
||||
resetTimer('productsFiler', 1000, function() {
|
||||
// preview
|
||||
if (xhr) {
|
||||
xhr.abort();
|
||||
}
|
||||
xhr = $.get('launch.php?s=ProductsFilter.php&acn=loadProducts&' + $form.serialize(), function(data) {
|
||||
$count.text(data.count);
|
||||
filterValue = data.filter;
|
||||
$products.html('');
|
||||
if (data.count > 0) {
|
||||
$.each(data.products, function(index, product) {
|
||||
$products.append(createProductMarkup(product));
|
||||
});
|
||||
}
|
||||
$button.removeClass('is-submitting');
|
||||
$preview.slideDown();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
{if $productsSortable}
|
||||
$form.on('orderChanged', function (event) {
|
||||
loadProducts();
|
||||
});
|
||||
{/if}
|
||||
$form.on('change input', ':input', function(event) {
|
||||
// Ignore changes from products autocomplete
|
||||
if ($(event.target).is('.chosen-search-input')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
loadProducts();
|
||||
});
|
||||
|
||||
loadProducts();
|
||||
});
|
||||
{/block}
|
||||
</script>
|
||||
@@ -0,0 +1,207 @@
|
||||
<div id="flapSearch" class="tab-pane boxStatic box">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<p>{'filtersIntroText'|translate:'sections'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row m-b-1">
|
||||
<div class="col-xs-6 d-flex align-items-center">
|
||||
{if $tab.data.type == 'sections' && $tab.data.ID > 0}
|
||||
{print_toggle attrs="id='data[inherit]'" nameRaw="data[inherit]" value=$tab.data.inherit}
|
||||
<label for="data[inherit]">{'filtersInherit'|translate:'sections'}</label>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6 d-flex align-items-center justify-content-end">
|
||||
{print_toggle attrs="id='hideInactive'" nameRaw="hideInactive" disabled=($tab.data.inherit == "Y")}
|
||||
<label for="hideInactive">{'filtersHideInactive'|translate:'sections'}</label>
|
||||
<script type="text/javascript">
|
||||
$(() => {
|
||||
function getInactiveItems() {
|
||||
return $('[data-filter-item-enabler]').filter((i, el) => !$(el).prop('checked')).closest('[data-filter-item]');
|
||||
}
|
||||
|
||||
$('input[name="hideInactive"]').on('change', function () {
|
||||
getInactiveItems().toggleClass('hidden')
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wpj-panel wpj-panel-default">
|
||||
<div class="wpj-panel-heading">
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<span class="drag-drop-mover"><i class="bi bi-arrows-move handle"></i></span>
|
||||
<small>{'name'|translate:'sections'}</small>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<small>{'type'|translate:'sections'}</small>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<small>{'filterEnable'|translate:'sections'}</small>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
</div>
|
||||
{ifmodule CONVERTORS}
|
||||
<div class="col-xs-2">
|
||||
<small>{'filterVariationConvertor'|translate:'sections'}</small>
|
||||
</div>
|
||||
{/ifmodule}
|
||||
</div>
|
||||
</div>
|
||||
<div class="wpj-list-group m-b-0" id="sorter" disabled>
|
||||
{foreach from=$tab.data.filterItems item=filter key=key}
|
||||
{if $tab.data.inherit == "Y" and $filter.enabled != "Y"}
|
||||
{continue}
|
||||
{/if}
|
||||
{* String parametry nejdou použít jako filtr *}
|
||||
{if $filter.type == 'parameter' && $filter.value_type == 'char' && $filter.enabled != "Y"}
|
||||
{continue}
|
||||
{/if}
|
||||
{if $filter.type == 'sellers' && $filter.enabled != "Y" && findModule('sellers','my_seller')}
|
||||
{continue}
|
||||
{/if}
|
||||
<div class="wpj-list-group-item{if $tab.data.inherit == "Y" and $filter.enabled == "Y"} disabled{/if}" data-filter-item="{$key}">
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
{if $filter.id and ($filter.id_source_section == $tab.data.ID or $filter.id_source_producer == $tab.data.ID)}
|
||||
<input type="hidden" name="data[filters][{$key}][id]" value="{$filter.id}" />
|
||||
{/if}
|
||||
<input type="hidden" name="data[filters][{$key}][type]" value="{$filter.type}" />
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="data[filters][{$key}][{if $tab.data.type == 'sections'}id_source_section{else}id_source_producer{/if}]"
|
||||
value="{$tab.data.ID}"
|
||||
/>
|
||||
|
||||
{if $filter.type == 'parameter'}
|
||||
<input type="hidden" name="data[filters][{$key}][id_parameter]" value="{$filter.id_parameter}" />
|
||||
{elseif $filter.type == 'variation'}
|
||||
<input type="hidden" name="data[filters][{$key}][id_variation_label]" value="{$filter.id_variation_label}" />
|
||||
{/if}
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="hidden" name="data[filters][{$key}][position]" value="{$filter.position|default:-1}"
|
||||
data-sort=""/>
|
||||
<span class="drag-drop-mover{if $filter.enabled != "Y"} disabled{/if}">
|
||||
<i class="bi bi-arrows-move handle"></i>
|
||||
</span>
|
||||
<strong>
|
||||
{if !$filter.name && $filter.type}
|
||||
{"filter_`$filter.type`"|translate:'filters'}
|
||||
{else}
|
||||
{$filter.name}
|
||||
{/if}
|
||||
</strong>
|
||||
|
||||
{if $filter.type == 'parameter' and $filter.value_type == 'char'}
|
||||
<a class="help-tip" data-toggle="tooltip" title="" data-original-title="Parameter typu "text" není podporován a proto nebude v sekci zobrazen.">
|
||||
<span class="list-no bi bi-x-circle-fill"></span>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
{if $filter.type == 'variation'}
|
||||
<span class="d-flex align-items-center">
|
||||
<i class="bi bi-lg bi-diagram-2 m-r-2 text-muted"></i>{'typeVariation'|translate:'sections'}
|
||||
</span>
|
||||
{elseif $filter.type == 'parameter'}
|
||||
<span class="d-flex align-items-center">
|
||||
<i class="bi bi-lg bi-sliders m-r-2 text-muted"></i>{'typeParameter'|translate:'sections'}
|
||||
</span>
|
||||
{else}
|
||||
<span class="d-flex align-items-center">
|
||||
<i class="bi bi-lg bi-gear m-r-2 text-muted"></i>{'typeOther'|translate:'sections'}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
{print_toggle nameRaw="data[filters][`$key`][enabled]" value=$filter.enabled attrs="data-filter-item-enabler=\"`$key`\"" disabled=($tab.data.inherit == "Y")}
|
||||
{if $tab.data.inherit == "Y" and $filter.enabled == "Y"}
|
||||
<input type="hidden" name="data[filters][{$key}][enabled]" value="{$filter.enabled}">
|
||||
{/if}
|
||||
<script type="text/javascript">
|
||||
$(() => {
|
||||
$('input[name="data[filters][{$key}][enabled]"]').on('change', function () {
|
||||
$(this).closest('[data-filter-item]').find('.drag-drop-mover').toggleClass('disabled');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
{ifmodule INDEXED_FILTER}
|
||||
{if $filter.indexing_allowed|default:true and ($filter.type == 'parameter' or $filter.type == 'variation' or $filter.type == 'producer')}
|
||||
<div class="d-flex justify-content-between" style="max-width: 260px;">
|
||||
<div class="checkbox">
|
||||
<input type="hidden" name="data[filters][{$key}][indexing]" value="N">
|
||||
<input type="checkbox" class="check" name="data[filters][{$key}][indexing]" id="PRMINDEX{$key}"
|
||||
value="Y" {if $filter.indexing == 'Y'}checked="checked"{/if} />
|
||||
|
||||
<label for="PRMINDEX{$key}">
|
||||
{'Index'|translate:'filters'}
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input type="hidden" name="data[filters][{$key}][to_title]" value="N">
|
||||
<input type="checkbox" class="check" name="data[filters][{$key}][to_title]" id="PRMTITLE{$key}"
|
||||
value="Y" {if $filter.to_title == 'Y'}checked="checked"{/if} />
|
||||
<label for="PRMTITLE{$key}">
|
||||
{'toTitle'|translate:'filters'}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/ifmodule}
|
||||
</div>
|
||||
{ifmodule CONVERTORS}
|
||||
<div class="col-xs-2">
|
||||
{if $filter.type == 'variation'}
|
||||
<select
|
||||
class="selecter"
|
||||
name="data[filters][{$key}][convertor]" data-type="convertor"
|
||||
data-preload="Convertors"
|
||||
data-filter-type="autocomplete"
|
||||
data-autocomplete="Convertors"
|
||||
data-autocomplete-params="allow_empty=1"
|
||||
>
|
||||
{if $filter.convertor}
|
||||
<option value="{$filter.convertor}" selected>{$filter.convertor}</option>
|
||||
{/if}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
{/ifmodule}
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
sort_section('#sorter');
|
||||
|
||||
function sort_section(name) {
|
||||
$(name).sortable({
|
||||
helper: function (e, row) {
|
||||
var $row = $(row);
|
||||
var $helper = $row.clone().addClass('drag-drop');
|
||||
return $helper[0];
|
||||
},
|
||||
stop: function (event, ui) {
|
||||
$(this).children().each(function (index, item) {
|
||||
$(item).find('[data-sort]').val($(item).index()).change();
|
||||
});
|
||||
|
||||
window.somethingChanged = true;
|
||||
},
|
||||
handle: '.handle',
|
||||
placeholder: 'placeholder',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
Reference in New Issue
Block a user