select('v.vat, p.discount, p.code, p.price') ->from('products', 'p') ->leftJoin('p', 'vats', 'v', 'p.vat = v.id') ->where(Operator::equals(['p.id' => $ID])); if (findModule(Modules::PRODUCTS, Modules::SUB_UNITS)) { $productQb->addSelect('pu.short_name_admin as unit') ->leftJoin('p', 'products_units', 'pu', 'p.unit = pu.id'); $productQb->addSelect('pu2.short_name_admin measure_quantity_name') ->leftJoin('p', 'products_units', 'pu2', 'p.measure_unit = pu2.id'); } $PRODUCT = $productQb->execute()->fetch(); if (empty($PRODUCT['unit'])) { $PRODUCT['unit'] = 'ks'; } $acn = getVal('acn', null, 'show'); // Categorization $CHOICES = []; $VALUES = []; $CHOICES_CATEGORIZED = []; $query = 'SELECT id, label ' .'FROM '.getTableName('products_variations_choices_labels').' AS pvcl ' .'ORDER BY id ASC'; $SQL = sqlQuery($query); foreach ($SQL as $row) { $CHOICES[$row['id']] = $row['label']; } // ------------ // uz pouzite $query = 'SELECT id_product, id_label, list_order ' .'FROM '.getTableName('products_variations_choices_categorization').' AS pvcc ' ."WHERE id_product={$ID} " .'ORDER BY list_order ASC'; $SQL = sqlQuery($query); foreach ($SQL as $row) { $CHOICES_CATEGORIZED[] = $row['id_label']; } // Values $CHOICES_VALUES = []; for ($x = 0; $x < count($CHOICES_CATEGORIZED); $x++) { $SQL = sqlQuery('SELECT id, value, code FROM '.getTableName('products_variations_choices_values').' AS pvcv WHERE id_label='.intval($CHOICES_CATEGORIZED[$x]).' ORDER BY sort ASC'); foreach ($SQL as $row) { $value = $row['value']; $id = $row['id']; $VALUES[$id] = $value; $CHOICES_VALUES[$CHOICES_CATEGORIZED[$x]][$id] = $value; } } // ############################################################### // nejdrive nacist vsechy kombinace k variantam $COMBINATIONS = []; $SQL = sqlQuery('SELECT pvc.id_variation, pvc.id_label, pvc.id_value FROM '.getTableName('products_variations_combination').' AS pvc JOIN '.getTableName('products_variations')." AS pv ON pv.id=pvc.id_variation WHERE pv.id_product={$ID}"); foreach ($SQL as $row) { $IDvariation = $row['id_variation']; if (!isset($COMBINATIONS[$IDvariation])) { $COMBINATIONS[$IDvariation] = []; } $COMBINATIONS[$IDvariation][] = $row; } $priceWithVat = $GLOBALS['dbcfg']['prod_prefer_price_vat']; $vatPerc = 0; $discount = 0; if ($priceWithVat == 'Y') { $vatPerc = $PRODUCT['vat']; } if ($priceWithVat == 'F') { $vatPerc = $PRODUCT['vat']; $discount = $PRODUCT['discount']; } $SQL = sqlQuery('SELECT pv.*, COALESCE(pv.price, p.price) as price_real FROM '.getTableName('products_variations').' AS pv LEFT JOIN '.getTableName('products').' p ON pv.id_product=p.id LEFT JOIN '.getTableName('products_variations_combination').' pvc ON pvc.id_variation=pv.id LEFT JOIN '.getTableName('products_variations_choices_values')." pvcv ON pvc.id_value=pvcv.id WHERE id_product={$ID} GROUP BY pv.id ORDER BY pvcv.sort ASC"); $VARIATIONS = []; foreach ($SQL as $key => $row) { $row['price_orig'] = $row['price']; if ($row['price']) { $row['price'] = calcPrice($row['price'], $vatPerc, $discount); // $row['price'] = round(setPrice($row['price'], $discount, $vatPerc, "format=false"), 2); } $row['price_real'] = calcPrice($row['price_real'], $vatPerc, $discount); $row['in_store'] = floatval($row['in_store']); $row['delivery_time_days'] = ''; if ($row['delivery_time'] > 0) { $row['delivery_time_days'] = $row['delivery_time']; $row['delivery_time'] = 1; } $row['piecesOrdered'] = returnSQLResult('SELECT SUM(oi.pieces) FROM '.getTableName('order_items').' oi LEFT JOIN '.getTableName('orders')." o ON oi.id_order=o.id WHERE oi.id_product={$ID} AND oi.id_variation={$row['id']} AND o.status_storno=0 AND o.status IN (".join(',', getStatuses('notpacked')).')'); $row['class'] = 'row-green'; if ($row['in_store'] < 0) { if ($row['id_product']) { // Suppliers products if (findModule('products_suppliers')) { $query = 'SELECT SUM(in_store) FROM '.getTableName('products_of_suppliers')." pos WHERE pos.id_product={$row['id_product']} AND pos.id_variation={$row['id']}"; $row['piecesInSuppliers'] = returnSQLResult($query); } } if ($row['in_store'] + $row['piecesOrdered'] < 0) { $row['class'] = 'row-red'; if (findModule('products_suppliers')) { if ($row['in_store'] + $row['piecesOrdered'] + $row['piecesInSuppliers'] > 0) { $row['class'] = 'row-orange'; } } } } $VARIATIONS[] = $row; } $pageVars = array_merge($pageVars, [ 'acn' => $acn, 's' => getVal('s'), 'ID' => $ID, 'choices' => $CHOICES, 'choices_categorized' => $CHOICES_CATEGORIZED, 'choices_values' => $CHOICES_VALUES, 'values' => $VALUES, 'variations' => $VARIATIONS, 'combinations' => $COMBINATIONS, 'product' => $PRODUCT, ]); if (!empty($this->getErrors()) || !empty($this->getHTMLErrors())) { $variationsData = getVal('variations', getVal('data')); foreach ($pageVars['variations'] as $key => &$variation) { if (!empty($variationsData[$key + 1])) { $variation = array_merge($variation, $variationsData[$key + 1]); } } } $pageVars['variationTranslationsFigure'] = $this->getVariationTranslationsFigure( array_map(fn ($x) => $x['id'], $pageVars['variations'] ?? []) ); $this->deserializeVariationsCustomData($pageVars['variations']); $vars['body'] = $pageVars; return $vars; } public function handleGenerateEan() { if ($id_variation = intval(getVal('id_variation'))) { $ean = EANValidator::generateEan(['id_variation' => $id_variation]); $exists = Variations::eanExists($id_variation, $ean); if ($exists) { $message = Variations::eanExistsMessage($exists); $this->addHTMLError('Vygenerovaný '.$message); // Vygenerovaný EAN {ean} není unikátní. {odkaz na duplicitní produkt} } else { sqlQueryBuilder()->update('products_variations') ->directValues(['ean' => $ean]) ->where(Operator::equals(['id' => $id_variation])) ->execute(); $this->returnOK(); } } } public function handleDeactivatePriceForDiscount() { if ($id_variation = intval(getVal('id_variation'))) { sqlQueryBuilder()->update('products_variations') ->directValues(['price_for_discount' => -1]) ->where(Operator::equals(['id' => $id_variation])) ->execute(); } $this->returnOK(); } public function handleActivatePriceForDiscount() { if ($id_variation = intval(getVal('id_variation'))) { sqlQueryBuilder()->update('products_variations') ->directValues(['price_for_discount' => null]) ->where(Operator::equals(['id' => $id_variation])) ->execute(); } $this->returnOK(); } public function handle() { parent::handle(); $ID = intval(getVal('ID')); $SQL = sqlQuery('SELECT v.vat, p.discount FROM '.getTableName('products')." p LEFT JOIN vats v ON p.vat=v.id WHERE p.id={$ID}"); $PRODUCT = sqlFetchArray($SQL); if (isset($_REQUEST['submitAddChoice']) && is_numeric($ID)) { $id_label = getVal('id_label'); if ($id_label) { sqlQuery('REPLACE INTO products_variations_choices_categorization SET id_product = :id_product, id_label = :id_label, list_order = :list_order', [ 'id_product' => $ID, 'id_label' => $id_label, 'list_order' => intval(getVal('list_order')), ]); $id_label_value = getVal('id_label_value'); if ($id_label_value) { $variations = sqlQuery('SELECT id FROM products_variations WHERE id_product = :id_product', ['id_product' => $ID]); foreach ($variations as $variation) { sqlQueryBuilder()->insert('products_variations_combination') ->directValues([ 'id_variation' => $variation['id'], 'id_label' => $id_label, 'id_value' => $id_label_value, ])->execute(); } } $ErrStr = getTextString('status', 'saved'); $url = 'launch.php?s='.$_REQUEST['s'].'&ID='.$_REQUEST['ID'].'&ErrStr='.urlencode($ErrStr); redirect($url); } $this->returnError('Nebyla vybrána jmenovka.'); } if (isset($_REQUEST['submitDeleteChoice']) && findRight('PROD_ERASE')) { $id_label = getVal('id_label_del'); if ($id_label) { sqlQuery('DELETE FROM products_variations_choices_categorization WHERE id_product = :id_product AND id_label = :id_label', [ 'id_product' => $ID, 'id_label' => $id_label, ]); sqlQuery('DELETE pvc FROM products_variations_combination pvc LEFT JOIN products_variations pv ON pvc.id_variation = pv.id WHERE pv.id_product = :id_product AND id_label = :id_label', [ 'id_product' => $ID, 'id_label' => $id_label, ]); $ErrStr = getTextString('status', 'saved'); $url = 'launch.php?s='.$_REQUEST['s'].'&ID='.$_REQUEST['ID'].'&ErrStr='.urlencode($ErrStr); redirect($url); } $this->returnError('Nebyla vybrána jmenovka.'); } if (isset($_REQUEST['submitGenerateCombinations']) && is_numeric($_REQUEST['ID'])) { $labels = []; $qb = sqlQueryBuilder()->select('pvcv.id_label, pvcv.id') ->from('products_variations_choices_categorization', 'pvcc') ->join('pvcc', 'products_variations_choices_values', 'pvcv', 'pvcv.id_label = pvcc.id_label') ->andWhere(Operator::equals(['id_product' => $ID])) ->orderBy('list_order', 'ASC'); foreach ($qb->execute() as $row) { $labels[$row['id_label']][$row['id']] = $row['id']; } $combinations = [[]]; foreach ($labels as $id_label => $values) { $arr = []; foreach ($combinations as $combination) { foreach ($values as $value) { $arr[] = array_replace($combination, [$id_label => $value]); } } $combinations = $arr; } foreach ($combinations as $combination) { sqlQueryBuilder()->insert('products_variations')->directValues(['id_product' => $ID])->execute(); $id = sqlInsertID(); foreach ($combination as $id_label => $id_value) { sqlQueryBuilder()->insert('products_variations_combination')->directValues([ 'id_variation' => $id, 'id_label' => $id_label, 'id_value' => $id_value, ])->execute(); } Variations::updateTitle($id); } $count = count($combinations); addActivityLog(ActivityLog::SEVERITY_SUCCESS, ActivityLog::TYPE_CHANGE, sprintf(translate('activityEdited', 'products'), $this->getID(), "Akce [Vygenerovat všechny možné varianty] byla provedena. Úspěšně vygenerovaných variant: {$count}.") ); $this->returnOK(); } $data = getVal('data'); $variations_data = getVal('variations', $data, []); $OK = false; foreach ($variations_data as $id => $var) { $var['id_product'] = $ID; if (!empty($var['delete']) || !$id) { if ($id > 0) { $this->deleteVariation($var); } continue; } $var['price'] = $this->preparePrice($var['price']); if ($var['priceWithVat'] == 'Y') { $var['price'] = $var['price'] / (1 + ($PRODUCT['vat'] / 100)); } if ($var['priceWithVat'] == 'F') { $var['price'] = $var['price'] / (1 + ($PRODUCT['vat'] / 100)) / (1 - ($PRODUCT['discount'] / 100)); } if (isset($var['code']) && empty($var['code'])) { $var['code'] = null; } if (!empty($var['weight'])) { $var['weight'] = str_replace(',', '.', $var['weight']); } if (!empty($var['measure_quantity'])) { $var['measure_quantity'] = str_replace(',', '.', $var['measure_quantity']); } foreach (['width', 'height', 'depth'] as $dimension) { $value = mb_strlen($var[$dimension] ?? '') > 0 ? $var[$dimension] : null; $var[$dimension] = isset($value) ? $this->preparePrice($value) : null; } if (findModule(Modules::BONUS_PROGRAM) && $var['bonus_points'] == '') { $var['bonus_points'] = null; } $var['in_store'] = str_replace(',', '.', $var['in_store']); if (!getVal('inStoreOverride', $var)) { unset($var['in_store']); } if (isset($var['in_store_min'])) { $this->prepareNull($var['in_store_min']); } $dbVariation = $this->fetchObject('products_variations', $var['id']); $this->unserializeCustomData($dbVariation); $var['data'] = array_merge($dbVariation['data'], $var['data'] ?? []); $this->serializeCustomData($var); try { if ($id < 0) { $this->setVariation($var); } else { $this->updateVariation($var); // zalogovat, ze byla rucne zmenena skladovost if (($var['in_store'] ?? false) !== false) { addActivityLog( ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_CHANGE, sprintf( translate('activityEdited', 'products'), $this->getID(), sprintf(translate('activityVariationInStoreEdited', 'products'), $dbVariation['title'], $dbVariation['in_store'] ?? 0, $var['in_store']) ) ); } } // pokud na variante se nastavila cena (napr. kvuli zlevneni konkretni varianty) // a CPS je prazdna (protoze byla prazdna cena = dedila se z produktu), // nastavit CPS varianty = CPS produktu if (findModule(Modules::PRICE_HISTORY) && empty($dbVariation['price_for_discount']) && !empty($var['price'])) { sqlQueryBuilder()->update('products_variations', 'pv') ->join('pv', 'products', 'p', 'p.id = pv.id_product') ->set('pv.price_for_discount', 'IF(p.price_for_discount > 0, p.price_for_discount, null)') ->where(Operator::equals(['pv.id' => $var['id']])) ->execute(); } } catch (Exception $e) { switch (intval($e->getPrevious()->errorInfo[1])) { case 1062: $ErrStr = 'Chyba při ukládání: Duplicitní '; $badFields = $this->getBadValues($var, ['id' => $this->ID]); foreach ($badFields as $value) { $ErrStr .= '{'.$value.'} '; } if (!$badFields) { $ErrStr = "Duplicitní záznam: \n".$e->getMessage(); } $this->addError($ErrStr); break; default: throw $e; } } $OK = true; } if (isset($_REQUEST['submitCopy']) && is_numeric($_REQUEST['ID']) && is_numeric($_REQUEST['productID'])) { Variations::duplicateVariations(intval($_REQUEST['productID']), intval($_REQUEST['ID'])); } // ############################################ // # Aktualizace fulltextového vyhledávání if (findModule(\Modules::SEARCH) && getVal('submitUpdateVariation') && !findModule(Modules::KAFKA)) { $fulltext = ServiceContainer::getService(FulltextInterface::class); $fulltext->updateProduct($ID); } if ($OK && empty($this->getErrors()) && empty($this->getHTMLErrors())) { $this->returnOK(); } } public function getBadValues($variation, $rowIdentifier = []) { $uniques = ['ean' => true]; if (findModule(Modules::PRODUCTS_VARIATIONS, Modules::SUB_CODE)) { $uniques['code'] = true; } $where = ''; foreach ($rowIdentifier as $key => $value) { $where .= " AND {$key}!=:{$key}"; } $data = $variation; $badFields = []; foreach ($uniques as $key => $value) { if ($value) { if (isset($data[$key])) { $SQL = returnSQLResult('SELECT COUNT(*) FROM '.getTableName('products_variations')." WHERE {$key}=:{$key} {$where}", array_merge($data, $rowIdentifier)); if ($SQL > 0) { $badFields[] = $key; } } } } return $badFields; } public function deleteVariation($variation) { $this->deleteSQL('products_variations', ['id' => $variation['id']]); $prod = new Product(intval($_REQUEST['ID'])); $prod->updateInStore(); $prod->updateDeliveryTime(); // ############################################ // # Aktualizace fulltextového vyhledávání if (findModule(\Modules::SEARCH) && !findModule(Modules::KAFKA)) { $fulltext = ServiceContainer::getService(FulltextInterface::class); $fulltext->updateProduct(getVal('ID')); } } public function updateVariation($variation) { global $cfg; $ID = $this->getID(); if ($variation['delivery_time'] == '1') { $variation['delivery_time'] = intval($variation['delivery_time_days']); } if ($variation['price'] <= 0) { $variation['price'] = null; if (findModule(Modules::PRICE_HISTORY)) { $variation['price_for_discount'] = null; } } if ($variation['ean'] <= 0) { $variation['ean'] = null; } if (isset($variation['weight'])) { $this->prepareNull($variation['weight']); } if (isset($variation['measure_quantity'])) { $this->prepareNull($variation['measure_quantity']); } if (isset($variation['price_buy'])) { $variation['price_buy'] = (empty($variation['price_buy']['value']) ? null : $this->prepareVatPrice($variation['price_buy'])); } if (isset($variation['price_common'])) { // if empty string or negative number, set null $variation['price_common'] = trim($variation['price_common']); $variation['price_common'] = $this->preparePrice($variation['price_common']); if ($variation['price_common'] < 0) { $variation['price_common'] = null; } } if (!empty($cfg['Modules']['products_variations']['variationCode'])) { $this->prepareNull($variation['code']); if ($variation['code']) { $uniqueCode = Variations::makeCodeUnique($variation['id'], $variation['code']); if ($uniqueCode != $variation['code']) { $this->addError("Kód {$variation['code']} není unikátní a byl změněn na {$uniqueCode}!"); $variation['code'] = $uniqueCode; } } } if (!empty($variation['ean'])) { if (Settings::getDefault()->admin_ean_check === 'Y' && !EANValidator::checkEAN($variation['ean']) ) { $this->addHTMLError("EAN {$variation['ean']} není validní (kód varianty: {$variation['code']})."); } $exists = Variations::eanExists($variation['id'], $variation['ean']); if ($exists) { $message = Variations::eanExistsMessage($exists); $this->addHTMLError($message); // EAN {ean} není unikátní. {odkaz na duplicitní produkt} $variation['ean'] = null; } } $this->updateSQL('products_variations', $variation, ['id' => $variation['id']], ['delete', 'changed', 'priceWithVat', 'delivery_time_days', 'inStoreOverride', 'combination', 'translation_figure', 'translation_figure_checkbox']); $combinations = $variation['combination']; if ($combinations) { $this->deleteSQL('products_variations_combination', ['id_variation' => $variation['id']]); foreach ($combinations as $id_label => $id_value) { if (empty($id_value)) { continue; } $this->insertSQL('products_variations_combination', [ 'id_variation' => intval($variation['id']), 'id_label' => intval($id_label), 'id_value' => intval($id_value), ]); } } $this->updateVariationTranslationsFigure((int) $variation['id'], $variation); Variations::updateTitle($variation['id']); $prod = new Product($ID); $prod->updateInStore(); $prod->updateDeliveryTime(); } public function setVariation(&$variation) { global $cfg; $ID = $this->getID(); if ($variation['delivery_time'] == '1') { $variation['delivery_time'] = intval($variation['delivery_time_days']); } if ($variation['price'] <= 0) { $variation['price'] = null; } if ($variation['ean'] <= 0) { $variation['ean'] = null; } // Prepare value $variation['id_product'] = $ID; if (!empty($cfg['Modules']['products_variations']['variationCode'])) { $this->prepareNull($variation['code']); if ($variation['code']) { $uniqueCode = Variations::makeCodeUnique(0, $variation['code']); if ($uniqueCode != $variation['code']) { $this->addError("Kód {$variation['code']} není unikátní a byl změněn na {$uniqueCode}!"); $variation['code'] = $uniqueCode; } } } if (isset($variation['weight'])) { $this->prepareNull($variation['weight']); } if (isset($variation['measure_quantity'])) { $this->prepareNull($variation['measure_quantity']); } $this->insertSQL('products_variations', $variation, ['delete', 'priceWithVat', 'combination', 'changed', 'delivery_time_days', 'inStoreOverride', 'translation_figure', 'translation_figure_checkbox']); $id = sqlInsertID(); $variation['id'] = $id; $combinations = $variation['combination']; foreach ($combinations as $id_label => $id_value) { $this->insertSQL('products_variations_combination', [ 'id_variation' => intval($id), 'id_label' => intval($id_label), 'id_value' => intval($id_value), ]); } $this->updateVariationTranslationsFigure((int) $id, $variation); $prod = new Product($ID); $prod->updateInStore(); $prod->updateDeliveryTime(); Variations::updateTitle($id); } public function deserializeVariationsCustomData(&$variations) { foreach ($variations as $key => $value) { $this->unserializeCustomData($variations[$key]); } } protected function updateVariationTranslationsFigure(int $variationId, array $variation): void { if (!findModule(Modules::TRANSLATIONS)) { return; } $this->getTranslationUtil()->updateTranslationsFigure( VariationsTranslation::class, $variationId, $variation['translation_figure'] ?? [] ); } protected function getVariationTranslationsFigure(array $variationIds): array { if (!findModule(Modules::TRANSLATIONS)) { return []; } return $this->getTranslationUtil()->getTranslationsFigure( VariationsTranslation::class, $variationIds ); } } $main_class = 'ProductsVariations';