'product', 'ReceivedOrder' => 'order', 'ReceivedOrderRow' => 'order', 'Brand' => 'producer', 'SeriesBrand' => 'serie', 'Parameter' => 'parameters', 'ParameterValue' => 'parameters_values', 'ParameterAssortmentGroup' => 'parameters_sections', 'ParameterStoreCard' => 'parameters_products', 'AssortmentGroup' => 'section', 'ComplementModel' => 'complement_model', 'ComplementTypeSC' => 'complement_type', 'Model' => 'model', 'Damage' => 'damage', 'Scent' => 'scent', 'ElementScentModel' => 'product_scent', 'Picture' => 'picture', 'UnitSize' => 'unitsize', 'SecondaryAssortmentGroup' => 'secondary_assortment_group', ]; public static $PRODUCT_FIELDS = [ 'ONDELETE' => 'delete', // Base 'Code' => 'code', 'StoreMenuItem_ID' => 'ignore', 'Quantity' => 'in_store', 'X_LimitWEB' => 'ignore', 'X_WEBGroup' => 'ignore', 'VOC_CZK' => 'ignore', 'ID' => 'ignore', 'EAN' => 'ean', 'X_VATRate' => 'vat', 'SeriesBrand_ID' => 'id_serie', 'StoreAssortmentGroup_ID' => 'id_section', 'ComplementModel_ID' => 'id_complement_model', 'ComplementTypeSC_ID' => 'id_complement_type', 'Annotation' => 'annotation', 'Brand_ID' => 'producer_id', // Popis 'X_Name_Mark' => 'ignore', // Parametr Produktová řada 'X_Note2' => 'ignore', 'X_Description' => 'descr', // Flagy 'X_Gift' => 'flag_G', // Flag dárek 'X_Action' => 'ignore', // Flag Akce 'X_Sample' => 'flag_V', // Flag Vzorek 'X_Tester' => 'flag_T', // Flag Tester 'X_Spatter' => 'flag_O', // Flag Odstrik 'X_Set' => 'flag_S', // Flag Set 'Quantity_ToDelivery' => 'ignore', // Flag Na ceste + parametry 'X_VOEU' => 'ignore', // Parametry 'X_SortCosmetics' => 'ignore', // Kategorie produktu + parametr 'X_Size' => 'ignore', // Parametr Velikost 'X_Damage' => 'ignore', // Parameter Poškození 'X_Damage_EN' => 'ignore', // Parameter Poškození 'X_sexB' => 'ignore', // Parametr Pohlaví 'X_Constitution' => 'constitution', // Parametr Složení 'X_Shade' => 'shade', // Parametr Odstín // Ignores // 'Description' => 'descr', 'UnitSize_ID' => 'ignore', 'SETtype' => 'ignore', 'Name2' => 'name2', // '' => 'ignore', 'X_SETtype' => 'ignore', 'X_SortCosmeticsPL' => 'ignore', 'X_SortCosmeticsEN' => 'ignore', 'X_LimitX_SortCosmeticsENWEB' => 'ignore', 'X_Note2EN' => 'ignore', 'X_Note2PL' => 'ignore', 'X_ShadeEN' => 'ignore', 'X_ShadePL' => 'ignore', 'X_Constitution_EN' => 'ignore', 'X_Constitution_PL' => 'ignore', 'X_Constitution_DE' => 'ignore', 'X_ShadeDE' => 'ignore', 'X_Note2DE' => 'ignore', 'X_EU' => 'ignore', 'X_IDpar' => 'ignore', 'X_Activate' => 'figure', 'X_WEB_Transfer' => 'ignore', 'X_WEB' => 'ignore', 'Depth' => 'ignore', 'Height' => 'ignore', 'Width' => 'ignore', // Prices 'MOC_EUR_GR' => 'ignore', 'MOC_EUR_PE' => 'ignore', 'VOC_PLN' => 'ignore', 'MOC_CZK_KZ' => 'price', 'MOC_CZK_PC' => 'ignore', 'MOC_CZK_PE' => 'ignore', 'VOC_EUR' => 'ignore', 'MOC_CZK_SP' => 'ignore', 'MOC_CZK_PV' => 'ignore', 'MOC_CHF' => 'ignore', 'MOC_CHF_PA' => 'ignore', 'MOC_BGN' => 'ignore', 'MOC_EUR_UA' => 'ignore', 'NAK_CZK_ES' => 'ignore', 'MOC_CZK_PE_DPH' => 'ignore', 'NAK_EUR_SI' => 'ignore', 'MOC_PLN_PP_DPH' => 'ignore', 'MOC_PLN_EG_DPH' => 'ignore', 'MOC_EUR_SL_DPH' => 'ignore', 'MOC_HRK_LI_DPH' => 'ignore', 'MOC_EUR_IT_DPH' => 'ignore', 'NAK_EUR_LI' => 'ignore', 'MOC_CZK_ES_DPH' => 'ignore', 'MOC_EUR_IT_DPH_12' => 'ignore', 'price_force' => 'price', 'price_history_force' => 'price_history', 'price_history_store' => 'price_history_store', 'Size' => 'size_value', 'X_Damage_PL' => 'ignore', 'X_Damage_ID' => 'damage', 'X_InstructionForUse' => 'ignore', 'X_Label' => 'ignore', 'LastID' => 'ignore', 'Weight' => 'weight', 'Width' => 'width', 'Height' => 'height', 'Depth' => 'depth', 'Video_URL' => 'video_url', // GPSR contact 'SupplierName' => 'sup_name', 'SupplierAddress' => 'sup_addr', 'SupplierContact' => 'sup_contact', ]; public static $PICTURE_FIELDS = [ 'StoreCard_ID' => 'id_product', 'PictureType' => 'type', 'ONDELETE' => 'delete', ]; public static $SCENT_FIELDS = [ 'Name' => 'name', 'ONDELETE' => 'delete', ]; public static $ORDER_FIELDS = [ 'Status' => 'status', 'Note_Expedition' => 'note_expedition', 'NoteEXPEDITION' => 'note_expedition', 'Note_CustomerService' => 'note_service', 'PackageNumber' => 'package_number', 'ReasonDescription' => 'note_storno', 'Quantity' => 'quantity', 'ExternalOrder' => 'ExternalOrder', ]; public static $SECONDARY_ASSORTMENT_GROUP_FIELDS = [ 'StoreCard_ID' => 'abra_product_id', 'StoreAssortmentGroup_ID' => 'abra_section_id', 'ONDELETE' => 'delete', ]; public static $ENABLED_STORES = [1, 6, 15, 50, 51]; public static array $STORES_HIERARCHY = [ [ 'store' => 1, 'price' => 1, 'sum_stores' => [1, 6, 15], ], ]; public static $EXTERNAL_SUPPLIER_ID = 0; // id from suppliers table public static $PRICE_LIMIT_FOR_DISCOUNT = 50000; protected $fulltext; public static $PICTURE_TYPES = [ '1' => 'nothing', '2' => 'Y', '3' => 'nothing', '4' => 'K', '5' => 'O', '6' => 'V', '10' => 'V', '11' => 'V', '12' => 'V', '13' => 'V', '14' => 'V', '15' => 'V', // Coty fotky '21' => 'Y', '22' => 'V', '23' => 'V', '24' => 'V', '25' => 'V', '26' => 'V', '27' => 'V', '28' => 'V', '29' => 'V', '30' => 'V', // Loreal fotky '31' => 'Y', '32' => 'V', '33' => 'V', '34' => 'V', '35' => 'V', '36' => 'V', '37' => 'V', '38' => 'V', '39' => 'V', '40' => 'V', // small_partners - Menší partneři - #9698, #9700 '41' => 'V', '42' => 'V', '43' => 'V', '44' => 'V', '45' => 'V', '46' => 'V', '47' => 'V', '48' => 'V', '49' => 'V', '50' => 'V', // small_partners_vip - Menší partneři (VIP) - #9779, #9830 '51' => 'Y', '52' => 'V', '53' => 'V', '54' => 'V', '55' => 'V', '56' => 'V', '57' => 'V', '58' => 'V', '59' => 'V', '60' => 'V', '360' => 'G', '361' => 'coty_video', // 360 degrees video '362' => 'coty_video', // campaign video 'default' => 'ignore', ]; public static $pictureType6AsLead = false; /** * [ABRA_UID => [Array of fields to sync]]. */ public static $TRANSLATION_FIELDS = [ 'AssortmentGroup' => [ 'Name', 'X_Description', 'AlternativeName', ], 'Damage' => [ 'Name', ], 'Brand' => [ 'X_Description', ], 'Scent' => [ 'Name', ], 'UnitSize' => [ 'Code', 'Name', ], 'ComplementTypeSC' => [ 'Name', ], 'Parameter' => [ 'Name', ], 'ParameterValue' => [ 'Name', ], 'SeriesBrand' => [ 'X_Description', ], 'Model' => [ 'X_Description', ], 'StoreCard' => [ 'Annotation', 'X_Description', 'X_Constitution', 'Video_URL', ], ]; public const CATEGORY_PARAMETER_ID = 28; public const MAIN_CATEGORY_PARAMETER_ID = 29; /** @var \KupShop\ElninoBundle\Util\RichContentUtil */ protected $richContentUtil; protected $package_sent_messages = [ BalikDoRuky::class => 'odeslano_cp', BalikNaPostu::class => 'odeslano_cp', Balikovna::class => 'odeslano_cp', BalikovnaNaAdresu::class => 'odeslano_cp', PPL::class => 'odeslano_ppl', PPLParcelShop::class => 'odeslano_ppl', Zasilkovna::class => 'odeslano_zasilkovna', ZasilkovnaDodaniNaAdresu::class => 'odeslano_zasilkovna', SPS::class => 'odeslano_sps', GLS::class => 'GLS', GLSParcelShop::class => 'GLS', ]; protected $siteAbbr = [ 'kosmetika-zdravi.cz' => 'kzcz', 'parfimo.ch' => 'fich', 'eglamour.de' => 'egde', 'parfimo.it' => 'fiit', 'spleticna.si' => 'spsi', 'parfumcity.ch' => 'pcch', 'parfimo.de' => 'fide', 'europarfemy.cz' => 'epcz', 'parfumcity.cz' => 'pccz', 'profumicitta.it' => 'prof', 'parfimo.at' => 'fiat', 'lijepa.hr' => 'lihr', ]; public function __construct($settings = null) { parent::__construct($settings); $this->fulltext = ServiceContainer::getService(FulltextInterface::class); $this->richContentUtil = ServiceContainer::getService(\KupShop\ElninoBundle\Util\RichContentUtil::class); } // Sync functions public function sync_model($code, $field, $value) { $abra_model = sqlFetchAssoc($this->selectSQL('abra_models', ['id_abra' => $code])); if (!$abra_model) { $this->insertSQL('abra_models', ['id_abra' => $code]); } if (empty(static::$MODEL_FIELDS[$field])) { return; } $column = static::$MODEL_FIELDS[$field]; if ($column == 'id_serie') { // update old_kupshop_id $abra_model = $this->selectSQL('abra_models', ['id_abra' => $code])->fetch(); if (empty($abra_model['old_kupshop_id'])) { if (isset($this->settings['old_urls']) && $this->settings['old_urls'] == true) { $maxId = sqlQuery('SELECT MAX(old_kupshop_id) as max_id FROM abra_models')->fetchColumn(); $this->updateSQL('abra_models', ['old_kupshop_id' => ($maxId + 1)], ['id_abra' => $code]); } else { if ($this->selectSQL('abra_models', ['old_kupshop_id' => $code])->fetch()) { $maxId = sqlQuery('SELECT MAX(old_kupshop_id) as max_id FROM abra_models')->fetchColumn(); $this->updateSQL('abra_models', ['old_kupshop_id' => ($maxId + 1)], ['id_abra' => $code]); } else { $this->updateSQL('abra_models', ['old_kupshop_id' => $code], ['id_abra' => $code]); } } } } elseif ($column == 'id_category') { // is cosmetics? /** @var Sortiment $util */ $util = ServiceContainer::getService(Sortiment::class); $sectionId = $this->selectSQL('abra_sections', ['id_abra' => $value], ['id_section'])->fetchColumn(); $cosmetics = 'Y'; if (in_array($sectionId, $util->getPerfumesSections())) { $cosmetics = 'N'; } $this->updateSQL('abra_models', ['cosmetics' => $cosmetics], ['id_abra' => $code]); } if ($column === 'id_richcontent') { $this->richContentUtil->handleSync('abra_models', $abra_model['id_abra'], $value, $this->lang); } return parent::sync_model($code, $field, $value); } public function sync_product($code, $field, $value, $value2 = null) { if ($code === '00000001') { return; } if (!isset(static::$PRODUCT_FIELDS[$field])) { return; } // TODO: Tohle sloucit s AbraElnino. Vetsina by toho mela byt stejna. Aspon vyhodit to co uz je v sync_products_switch $column = static::$PRODUCT_FIELDS[$field]; $id_product = $this->getMappingFromAbra('product', $code); if (!$id_product) { $vat_id = null; getVat($vat_id); $insert = [ 'code' => $code, 'title' => $code, 'vat' => $vat_id, 'delivery_time' => '-1', // 'date_added' => date('Y-m-d H:i:s'), // set date_added later in updateVisibility() #9593 'figure' => 'O', ]; if (ctype_digit($code)) { $insert['id'] = $code; $id_product = $code; } $this->insertSQL('products', $insert); // maxscale tady zlobi. Kdyz mu nasetuju IDcko natvrdo, nevrati mi ho v sqlInsertId() if (!$id_product) { $id_product = sqlInsertId(); } $this->updateSQL('products', ['code' => $code], ['id' => $id_product]); $this->setMappingFromAbra('product', $id_product, $code); } $id_model = returnSQLResult('SELECT id_model FROM products WHERE id = :id', ['id' => $id_product]); $value = trim($value); $msgLevel = $this->getMessageLevel(); if ($column === 'id_richcontent') { $this->richContentUtil->handleSync('products', $id_product, $value, $this->lang); } if ($this->isTranslationsSync()) { if (!$this->translationFieldSupported($field)) { return; } switch ($column) { case 'descr': $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $id_product], ['long_descr' => $value]); break; case 'annotation': $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $id_product], ['short_descr' => $value]); break; case 'constitution': $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $id_product], ['set' => $value]); break; case 'video_url': $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $id_product], ['video_url' => $this->parseNullable($value)]); break; default: $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $id_product], [$column => $value]); break; } $this->scheduleProductTitleUpdate($id_product, null, null, null, null, $id_model); return; } if (is_null($msgLevel)) { return false; } switch ($column) { case 'ean': $value = $this->parseNullable($value); $this->updateSQL('products', ['ean' => $value], ['id' => $id_product]); break; case 'flag_G': case 'flag_A': case 'flag_V': case 'flag_T': case 'flag_O': case 'flag_S': $flag = substr($column, 5); if ($flag == 'S') { $value = !empty($value); } else { $value = $this->parseBool($value); } $data = ['id_product' => $id_product, 'flag' => $flag]; $this->updateProductFlag($data, $value); $this->scheduleProductTitleUpdate($id_product); break; case 'damage': $value = $this->parseNullable($value); if (empty($value)) { $this->updateProductFlag(['id_product' => $id_product, 'flag' => 'P'], false); } else { $this->updateProductFlag(['id_product' => $id_product, 'flag' => 'P'], true); } $this->sync_products_common($code, $field, $value, $value2, $id_product); break; case 'price': if (!$value2) { $value2 = 1; } if (!in_array($value2, static::$ENABLED_STORES)) { var_dump("ignorace ceny skladu {$value2}."); return; } $value = $this->preparePriceFromAbra($value); $qb = sqlQueryBuilder()->select('p.data, p.price, p.display_discount, p.figure, p.price_for_discount') ->fromProducts()->where(Operator::equals(['p.id' => $id_product])); if (findModule(Modules::PRICE_HISTORY)) { $qb->addSelect('MAX(ph.price) as max_price') ->leftJoin('p', 'price_history', 'ph', 'p.id = ph.id_product AND ph.date_change >= DATE_SUB(CURDATE(), INTERVAL 21 DAY)'); } $product = $qb->execute()->fetch(); // Store new price to product custom data $data = json_decode($product['data'] ?? '[]', true); $data['elnino_prices'][$value2] = $value; // Get current product store $active_store = $this->getProductStoreHierarchy($data['elnino_stores'] ?? [], $data['elnino_prices'] ?? [], $data['elnino_storeActivations'] ?? []); $update_data = [ ]; $actual = $product['price']; $max_price = $product['max_price'] ?? $product['price']; if (($value != $actual || $max_price != $product['price_for_discount']) && $actual > 0 && $product['figure'] == 'Y' && isset($active_store['price']) && $active_store['price'] == $value2 ) { if (findModule(Modules::PRICE_HISTORY) && ($value > $max_price)) { $max_price = $value; $history = ['id_product' => $id_product, 'price' => $max_price, 'last' => 1, 'date_change' => date('Y-m-d')]; if (empty($product['max_price'])) { $this->insertSQL('price_history', $history); } else { $this->updateSQL('price_history', $history, ['id_product' => $id_product, 'last' => 1]); } } $diff = floor((1 - ($value / $max_price)) * 100); $change_discount = 0; if ($diff >= 3 && $actual < $this::$PRICE_LIMIT_FOR_DISCOUNT && $diff <= $this::$MAX_DISCOUNT) { $change_discount = 1; } else { if (findModule(Modules::PRICE_HISTORY)) { $change_discount = -1; } elseif ($diff < -3) { $change_discount = -1; } } $flagUpdateData = ['id_product' => $id_product, 'flag' => 'D']; if ($change_discount == 1) { $update_data['display_discount'] = $diff; $update_data['price_for_discount'] = $max_price; $this->updateProductFlag($flagUpdateData, true); sqlQueryBuilder()->update('products')->set('discount_date', ':date') ->setParameter('date', new DateTime(), \Doctrine\DBAL\Types\Type::DATETIME) ->where(\Query\Operator::equals(['id' => $id_product]))->execute(); } elseif ($change_discount == -1) { $update_data['display_discount'] = 0; $update_data['price_for_discount'] = $value; $this->updateProductFlag($flagUpdateData, false); sqlQueryBuilder()->update('products')->set('discount_date', ':null')->setParameter('null', null) ->where(\Query\Operator::equals(['id' => $id_product]))->execute(); } } if ($value != $actual && isset($active_store['price']) && $active_store['price'] == $value2) { $update_data['price'] = $value; } if (($active_store['store'] ?? null) != ($data['elnino_store'] ?? '')) { $data['elnino_store'] = $active_store['store'] ?? null; } $update_data['data'] = json_encode($data); $this->updateSQL('products', $update_data, ['id' => $id_product]); $this->scheduleProductTitleUpdate($id_product); break; case 'price_vat': $value = $this->preparePriceFromEUR($value); $value = toDecimal($value)->removeVat(getVat()); $this->sync_product($code, 'price_force', $value, $value2); break; case 'price_vat_history': $value = $this->preparePriceFromEUR($value); $value = toDecimal($value)->removeVat(getVat()); $this->sync_product($code, 'price_history_force', $value, $value2); break; case 'price_history': if (!$value2) { $value2 = 1; } if (!in_array($value2, static::$ENABLED_STORES)) { var_dump("ignorace ceny skladu {$value2}."); return; } $value = $this->preparePriceFromAbra($value); $qb = sqlQueryBuilder()->select("p.id, p.data, p.price, p.figure, p.price_for_discount, FIND_IN_SET('PARTNER', p.campaign) AS is_partner") ->fromProducts()->where(Operator::equals(['p.id' => $id_product])); $product = $qb->execute()->fetch(); // Store new price to product custom data $data = json_decode($product['data'] ?? '[]', true); $data['elnino_prices'][$value2] = $value; // Get current product store $stores = $data['elnino_stores'] ?? []; $active_store = $this->getProductStoreHierarchy($stores, $data['elnino_prices'] ?? [], $data['elnino_storeActivations'] ?? []); $update_data = [ ]; $actual = $product['price']; if ($value != $actual && $actual > 0 && $product['figure'] == 'Y' && isset($active_store['price']) && $active_store['price'] == $value2) { $priceForDiscount = $product['price_for_discount'] ?? $product['price']; // CPS if ($priceForDiscount == '0.0000') { $new_discount = 0.0; } else { $new_discount = floor((1 - ($value / $priceForDiscount)) * 100); } $flag_data = ['id_product' => $id_product, 'flag' => 'D']; if ($new_discount >= 3 && $actual < $this::$PRICE_LIMIT_FOR_DISCOUNT && $new_discount <= $this::$MAX_DISCOUNT) { $discount = $new_discount; $this->updateProductFlag($flag_data, true); } else { $discount = 0; $this->updateProductFlag($flag_data, false); } $update_data['display_discount'] = $discount; } if ($value != $actual && isset($active_store['price']) && $active_store['price'] == $value2) { $update_data['price'] = $value; } if (($active_store['store'] ?? null) != ($data['elnino_store'] ?? '')) { $data['elnino_store'] = $active_store['store'] ?? null; } $update_data['data'] = json_encode($data); $this->refreshPartnerFlag($active_store, $product); $this->refreshSupplierInStore($stores, $active_store, $id_product); $this->updateSQL('products', $update_data, ['id' => $id_product]); $this->scheduleProductTitleUpdate($id_product); break; case 'price_history_store': // Dočasná funkce, která dělá to samý co price_history, ale kašle na sklady // to když se sklady řeší na eshopu, ne v engine, ale chci mít v engine řešení slev $qb = sqlQueryBuilder()->select('p.price, p.figure, p.price_for_discount') ->fromProducts()->where(Operator::equals(['p.id' => $id_product])); $product = $qb->execute()->fetch(); $update_data = [ ]; $actual = $product['price']; if ($value != $actual && $actual > 0 && $product['figure'] == 'Y') { $priceForDiscount = $product['price_for_discount'] ?? $product['price']; // CPS if ($priceForDiscount == '0.0000') { $new_discount = 0.0; } else { $new_discount = floor((1 - ($value / $priceForDiscount)) * 100); } $flag_data = ['id_product' => $id_product, 'flag' => 'D']; if ($new_discount >= 3 && $actual < $this::$PRICE_LIMIT_FOR_DISCOUNT && $new_discount <= $this::$MAX_DISCOUNT) { $discount = $new_discount; $this->updateProductFlag($flag_data, true); } else { $discount = 0; $this->updateProductFlag($flag_data, false); } $update_data['display_discount'] = $discount; } $update_data['price'] = $value; if ($update_data) { $this->updateSQL('products', $update_data, ['id' => $id_product]); } break; case 'price_buy': $new_buy = $this->preparePriceFromEUR($value); $product = sqlFetchArray(sqlQuery( "SELECT price, price_buy FROM products WHERE id=:id AND NOT FIND_IN_SET('I', campaign)", ['id' => $id_product] )); if (!$product) { return; } $old_buy = (float) $product['price_buy']; $old_price = (float) $product['price']; if ($old_buy && $old_price) { $margin = $old_price / $old_buy; $new_price = $new_buy * $margin; $this->updateSQL('products', ['price_buy' => $new_buy, 'price' => $new_price], ['id' => $id_product]); } else { $this->updateSQL('products', ['price_buy' => $new_buy], ['id' => $id_product]); } $this->scheduleProductTitleUpdate($id_product); break; case 'price_buy_save_only': if (empty($value2) || $value2 == 1) { $new_buy = $this->preparePriceFromEUR($value); $this->updateSQL('products', ['price_buy' => $new_buy], ['id' => $id_product]); } break; case 'vat': $id_vat = returnSQLResult('SELECT id FROM vats WHERE vat=:vat', ['vat' => $value]); $this->updateSQL('products', ['vat' => $id_vat], ['id' => $id_product]); break; case 'descr': $this->updateSQL('products', ['long_descr' => $value], ['id' => $id_product]); break; case 'annotation': $this->updateSQL('products', ['short_descr' => $value], ['id' => $id_product]); break; case 'figure': if (!empty($value2)) { // product+web+store X_Activation sync $product = sqlFetchAssoc(sqlQuery("SELECT id, data, FIND_IN_SET('PARTNER', campaign) AS is_partner FROM products WHERE id=:id", ['id' => $id_product])); $data = json_decode($product['data'], true); $prices = $data['elnino_prices'] ?? []; $stores = $data['elnino_stores'] ?? []; $storeActivations = $data['elnino_storeActivations'] ?? []; if ($value === 'DEL' && isset($storeActivations[$value2])) { // product+web+store deactivation deletion, e.g: 2719494235~|~StoreCard~|~X_Activate~|~154323~|~DEL~|~1~|~kosmetika-zdravi.cz~|~all unset($storeActivations[$value2]); } elseif ($value !== 'DEL') { // set product+web+store deactivation, e.g: 2719494235~|~StoreCard~|~X_Activate~|~154323~|~Ne~|~1~|~kosmetika-zdravi.cz~|~all $storeActivations[$value2] = $this->parseBool($value); } // Get current product store $active_store = $this->getProductStoreHierarchy($stores, $prices, $storeActivations); $inStoreValue = array_sum( array_map(function ($id_store) use ($stores) { return $stores[$id_store] ?? 0; }, $active_store['sum_stores'] ?? []) ); $update_data = [ 'in_store' => $inStoreValue, ]; $this->refreshPartnerFlag($active_store, $product); $this->refreshSupplierInStore($stores, $active_store, $id_product); if (isset($active_store['price']) && ($prices[$active_store['price']] ?? false)) { $update_data['price'] = $prices[$active_store['price']]; } else { unset($update_data['price']); } if (($active_store['store'] ?? null) != ($data['elnino_store'] ?? '')) { $data['elnino_store'] = $active_store['store'] ?? null; } $data['elnino_stores'] = $stores; if (count($storeActivations)) { $data['elnino_storeActivations'] = $storeActivations; } else { unset($data['elnino_storeActivations']); } $update_data['data'] = json_encode($data); $logTransactionLevel = array_search($this->settings['web'], ['kosmetika-zdravi.cz', 'europarfemy.cz']) !== false; if (!isLocalDevelopment() && $logTransactionLevel) { $this->log([ 'store X_Activate transactionLevel' => sqlGetConnection()->getTransactionNestingLevel(), 'productID' => $id_product, 'value' => $value, 'value2' => $value2, 'queue' => $this->settings['queue'], ]); } sqlQueryBuilder()->update('products')->directValues($update_data) ->where(Operator::equals(['id' => $id_product])) ->execute(); $this->scheduleProductTitleUpdate($id_product, null, null, null, null, $id_model); break; } // $productLevel = returnSQLResult('SELECT figure_level FROM products WHERE id=:id', ['id' => $id_product]); // // if ($msgLevel < $productLevel) { // return false; // } // On delete, revert to zero if ($value === '' || $value === 'DEL') { $msgLevel = 0; } $value = $this->parseBool($value); if ($msgLevel == 3) { break; // ignore X_Activation per site (ignore legacy local deactivation) } $this->updateSQL('products', ['figure' => $value ? 'Y' : 'N', 'figure_level' => $msgLevel], ['id' => $id_product]); $this->scheduleProductTitleUpdate($id_product, null, null, null, null, $id_model); break; case 'in_store': if (!$value2) { var_dump('Neni cislo skladu!!!'); return null; } if (!in_array($value2, static::$ENABLED_STORES)) { var_dump("Ignorace skladu {$value2} !!!"); return null; } $value = intval($value); $product = sqlFetchAssoc(sqlQuery("SELECT id, data, FIND_IN_SET('PARTNER', campaign) AS is_partner FROM products WHERE id=:id", ['id' => $id_product])); $data = json_decode($product['data'], true); $prices = $data['elnino_prices'] ?? []; $stores = $data['elnino_stores'] ?? []; $stores[$value2] = $value; $storeActivations = $data['elnino_storeActivations'] ?? []; // Get current product store $active_store = $this->getProductStoreHierarchy($stores, $prices, $storeActivations); $value = array_sum( array_map(function ($id_store) use ($stores) { return $stores[$id_store] ?? 0; }, $active_store['sum_stores'] ?? []) ); $update_data = [ 'in_store' => $value, ]; $this->refreshPartnerFlag($active_store, $product); $this->refreshSupplierInStore($stores, $active_store, $id_product); if (isset($active_store['price']) && ($prices[$active_store['price']] ?? false)) { $update_data['price'] = $prices[$active_store['price']]; } else { unset($update_data['price']); } if (($active_store['store'] ?? null) != ($data['elnino_store'] ?? '')) { $data['elnino_store'] = $active_store['store'] ?? null; } $data['elnino_stores'] = $stores; $update_data['data'] = json_encode($data); sqlQueryBuilder()->update('products')->directValues($update_data) ->where(Operator::equals(['id' => $id_product])) ->execute(); break; case 'producer_id': $id_producer = $this->getMappingFromAbra('producer', $value); $this->updateSQL('products', ['producer' => $id_producer], ['id' => $id_product]); $this->scheduleProductTitleUpdate($id_product); break; case 'id_section': $this->sync_products_common($code, $field, $value, $value2, $id_product); break; case 'sex': $data = ['id_product' => $id_product, 'id_parameter' => 18]; $this->deleteSQL('parameters_products', $data); switch ($value) { case 'M': $value = 16; break; case '': case 'W': $value = 17; break; case 'U': $value = 22; break; case 'K': $value = 23; break; case 'B': $value = 24; break; default: throw new Exception('Nedefinované pohlaví: '.$value); } $data['value_list'] = $value; $this->insertSQL('parameters_products', $data); $this->scheduleProductTitleUpdate($id_product); return; case 'weight': if (findModule(Modules::PRODUCTS, Modules::SUB_WEIGHT)) { $value = $value == $this->abraNull ? null : Decimal::fromString($this->preparePriceFromAbra($value)); $this->updateSQL($this->main_table, [$column => $value], ['id' => $id_product]); } break; case 'width': case 'height': case 'depth': $value = $value == $this->abraNull ? null : Decimal::fromString($this->preparePriceFromAbra($value))->div(Decimal::fromInteger(10)); // comes in millimeters $this->updateSQL($this->main_table, [$column => $value], ['id' => $id_product]); break; case 'video_url': case 'sup_name': case 'sup_contact': $this->updateSQL('products', [$column => $this->parseNullable($value)], ['id' => $id_product]); break; case 'sup_addr': $value = trim($value) === ', ,' ? '' : $value; $this->updateSQL('products', [$column => $this->parseNullable($value)], ['id' => $id_product]); break; case 'ignore': break; case 'delete': $this->deleteSQL('products', ['id' => $id_product]); $this->scheduleProductTitleUpdate(null, null, null, null, null, $id_model); break; default: $this->sync_products_common($code, $field, $value, $value2, $id_product); return; } } protected function getProductStoreHierarchy(array $stock, array $prices, array $storeActivations) { foreach (static::$STORES_HIERARCHY as $store) { $pieces = $stock[$store['store']] ?? 0; $price = $prices[$store['price']] ?? 0; $storeActive = $storeActivations[$store['store']] ?? true; if ($pieces > 0 && $price > 0 && $storeActive) { return $store; } } return []; } public function updateVisibility($models, $products) { if ($products) { $allStoresDeactivatedJsonString = json_encode(array_fill_keys(static::$ENABLED_STORES, false)); $SQL = sqlQuery('UPDATE products p LEFT JOIN photos_products_relation AS ppr ON ppr.id_product = p.id AND ppr.show_in_lead = "Y" LEFT JOIN ( SELECT i_s.*, i_ps.id_product FROM products_in_sections i_ps INNER JOIN sections i_s ON i_s.id=i_ps.id_section AND i_s.figure = \'Y\' WHERE i_ps.id_product IN (:id_products) ) s ON s.id_product=p.id SET p.figure = \'O\' WHERE p.figure = \'Y\' AND ( ppr.id_photo IS NULL OR p.price <= 0 OR (damage IS NOT NULL AND p.in_store <= 0) OR p.id_model IS NULL OR s.id IS NULL OR JSON_CONTAINS(JSON_QUERY(p.data, \'$.elnino_storeActivations\'), :allStoresDeactivated) ) AND p.id IN (:id_products)', ['id_products' => $products, 'allStoresDeactivated' => $allStoresDeactivatedJsonString], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY] ); if (sqlAffectedRows($SQL) > 0) { var_dump(['Skryto produktů bez obrázků nebo nulovou cenou nebo bez modelu nebo vyprodaných poškozených krabiček', sqlAffectedRows($SQL)]); } $SQL = sqlQuery('UPDATE products p LEFT JOIN photos_products_relation AS ppr ON ppr.id_product = p.id AND ppr.show_in_lead = "Y" LEFT JOIN products_in_sections ps ON ps.id_product=p.id LEFT JOIN sections s ON s.id=ps.id_section AND s.figure = \'Y\' SET p.figure = \'Y\', p.date_added = IF(p.date_added IS NULL OR p.date_added = \'0000-00-00 00:00:00\', NOW(), p.date_added) WHERE p.figure = \'O\' AND ppr.id_photo IS NOT NULL AND p.price > 0 AND (damage IS NULL OR p.in_store > 0) AND (p.id_model IS NOT NULL) AND (s.id IS NOT NULL) AND NOT COALESCE(JSON_CONTAINS(JSON_QUERY(p.data, \'$.elnino_storeActivations\'), :allStoresDeactivated), FALSE) AND p.id IN (:id_products)', ['id_products' => $products, 'allStoresDeactivated' => $allStoresDeactivatedJsonString], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY] ); if (sqlAffectedRows($SQL) > 0) { var_dump(['Zobrazeno produktů s obrázkem a cenou a skladem poškozených krabiček', sqlAffectedRows($SQL)]); } if (!empty(static::$flagsToHide)) { $where = []; foreach (static::$flagsToHide as $flag) { $where[] = 'FIND_IN_SET("'.$flag.'", p.campaign)'; } $where = join(' OR ', $where); $SQL = sqlQuery( 'UPDATE products p SET p.figure = "N" WHERE p.figure = "Y" AND ('.$where.') AND p.id IN (:id_products)', ['id_products' => $products], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY] ); if (sqlAffectedRows($SQL) > 0) { var_dump(['Skryto produktů s flagem "O" (odstřik) nebo "V" (vzorek)'], sqlAffectedRows($SQL)); } } $gift = translate('gift', 'elnino', true) ?: 'dárek'; $SQL = sqlQuery('UPDATE products p SET p.name2 = CONCAT_WS(\' \', p.name2, :gift) WHERE p.id IN (:id_products) AND FIND_IN_SET(\'G\', p.campaign) AND p.name2 NOT LIKE :gift_all', ['id_products' => $products, 'gift' => $gift, 'gift_all' => "%{$gift}%"], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); if (sqlAffectedRows($SQL) > 0) { var_dump(['Přidáno doplnků názvu produktu jako "dárek"', sqlAffectedRows($SQL)]); } $SQL = sqlQuery("UPDATE products p SET p.name2 = REPLACE(REPLACE(p.name2, :gift, ''), ' ', ' ') WHERE p.id IN (:id_products) AND FIND_IN_SET('G', p.campaign) = 0 AND p.name2 LIKE :gift_all", ['id_products' => $products, 'gift' => $gift, 'gift_all' => "%{$gift}%"], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); if (sqlAffectedRows($SQL) > 0) { var_dump(['Odebráno doplňků názvu produktu jako "dárek"', sqlAffectedRows($SQL)]); } $SQL = sqlQuery('UPDATE products SET figure = "N" WHERE FIND_IN_SET("G", campaign) AND id IN (:id_products)', ['id_products' => $products], ['id_products' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); if (sqlAffectedRows($SQL) > 0) { var_dump(['Skryto produktů označených jako dárek'], sqlAffectedRows($SQL)); } } if ($models) { $SQL = sqlQuery('UPDATE products p JOIN ( SELECT am.id_abra, MAX(p.figure) as max_figure, am.id_photo FROM abra_models am JOIN products p ON p.id_model = am.id_abra WHERE am.id_abra IN (:id_models) GROUP BY am.id_abra HAVING max_figure = \'Y\' ) t ON p.id_model = t.id_abra SET p.figure = \'O\' WHERE p.figure = \'Y\' AND t.id_photo IS NULL', ['id_models' => $models], ['id_models' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY] ); if (sqlAffectedRows($SQL) > 0) { var_dump(['Skryto produktů bez obrázků nebo bez viditelné varianty', sqlAffectedRows($SQL)]); } $SQL = sqlQuery('UPDATE products p JOIN ( SELECT am.id_abra, MAX(p.figure) as max_figure, am.id_photo FROM abra_models am JOIN products p ON p.id_model = am.id_abra WHERE am.id_abra IN (:id_models) GROUP BY am.id_abra ) t ON p.id_model = t.id_abra LEFT JOIN photos_products_relation AS ppr ON ppr.id_variation = p.id AND ppr.show_in_lead = "N" SET p.figure = \'Y\' WHERE p.figure = \'O\' AND t.id_photo IS NOT NULL AND p.price > 0 AND FIND_IN_SET("G", p.campaign) = 0 AND ppr.id_photo IS NOT NULL', ['id_models' => $models], ['id_models' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY] ); if (sqlAffectedRows($SQL) > 0) { var_dump(['Zobrazeno produktů v "prodej ukončen" s obrázkem a cenou a viditelnou variantou', sqlAffectedRows($SQL)]); } } // // Update fulltext search index // if ($this->fulltext && $products) { // foreach ($products as $id_product) { // $this->fulltext->updateProduct($id_product); // } // } } public function updateScheduledProducts() { if (!empty($this->cwd)) { chdir($this->cwd); } $this->collectUpdatedProducts(); // Update products if ($product_list = $this->getDelayedUpdate('product')) { $productIds = array_keys($product_list); $this->updateVisibility(null, $productIds); $this->updateFeedPricesByContentAPI($productIds); // update products for each language foreach ($this->languageContext->getSupported() as $language) { $qb = sqlQueryBuilder() ->select('p.*, s.name AS section_name, pr.name AS producer_name, abs.name AS serie_name,abd.name AS damage_name, abcm.name AS complement_model_name, abct.name AS complement_type_name, ap.id_serie, ap.id_section, ap.id_complement_model, abs.id_producer, abs.id_serie AS id_serie_kupshop, abs.sex, abm.id_abra as id_model') ->from('products', 'p') ->leftJoin('p', 'abra_models', 'abm', 'abm.id_abra = p.id_model') ->leftJoin('ap', 'abra_sections', 'absec', 'absec.id_abra = abm.id_section') ->leftJoin('absec', 'sections', 's', 's.id = absec.id_section') ->leftJoin('ap', 'abra_complement_models', 'abcm', 'abcm.id_abra = abm.id_complement_model') ->leftJoin('p', 'abra_complement_types', 'abct', 'abct.id_abra = p.id_complement_type') ->leftJoin('p', 'abra_damages', 'abd', 'abd.id_abra = p.damage') ->leftJoin('p', 'abra_products', 'ap', 'p.id = ap.id_product') ->leftJoin('ap', 'abra_series', 'abs', 'abs.id_abra = ap.id_serie') ->leftJoin('abs', 'producers', 'pr', 'pr.id = abs.id_producer') ->andWhere(\Query\Operator::inIntArray($productIds, 'p.id')) ->groupBy('p.id'); if ($language->getId() != $this->languageContext->getDefaultId()) { // join translations $qb->andWhere( \Query\Translation::coalesceTranslatedFields( \KupShop\I18nBundle\Translations\SectionsTranslation::class, ['name' => 'section_name'], $language->getId() ) )->andWhere( \Query\Translation::coalesceTranslatedFields( \KupShop\ElninoBundle\Translations\AbraDamagesTranslation::class, ['name' => 'damage_name'], $language->getId() ) )->andWhere( \Query\Translation::coalesceTranslatedFields( \KupShop\ElninoBundle\Translations\AbraModelsTranslation::class, null, $language->getId() ) )->andWhere( \Query\Translation::coalesceTranslatedFields( \KupShop\ElninoBundle\Translations\AbraComplementTypeTranslation::class, ['name' => 'complement_type_name'], $language->getId() ) )->andWhere( \Query\Translation::coalesceTranslatedFields( \KupShop\I18nBundle\Translations\ProductsTranslation::class, null, $language->getId() ) ); } foreach ($qb->execute() as $product) { try { $this->updateProduct($product, $language->getId()); } catch (Exception $e) { exception_handler($e); } } } $this->updateVisibility(false, $productIds); $this->deleteDelayedUpdate($product_list); } // Update models if ($model_list = $this->getDelayedUpdate('model')) { $modelIds = array_keys($model_list); $this->updateVisibility($modelIds, null); $qb = sqlQueryBuilder() ->select( 'abm.id_abra as id, s.name AS section_name, pr.name AS producer_name, abs.name AS serie_name, abcm.name AS complement_model_name, abs.sex, abm.id_section, pr.id AS id_producer, abs.id_abra AS id_serie, abm.descr AS long_descr' ) ->from('abra_models', 'abm') ->leftJoin('abm', 'abra_sections', 'absec', 'absec.id_abra = abm.id_section') ->leftJoin('absec', 'sections', 's', 's.id = absec.id_section') ->leftJoin('abm', 'abra_series', 'abs', 'abs.id_abra = abm.id_serie') ->leftJoin('abm', 'abra_complement_models', 'abcm', 'abcm.id_abra = abm.id_complement_model') ->leftJoin('abs', 'producers', 'pr', 'pr.id = abs.id_producer') ->andWhere(\Query\Operator::inIntArray($modelIds, 'abm.id_abra')) ->groupBy('abm.id_abra'); foreach ($qb->execute() as $model) { try { $this->updateModel($model); } catch (Exception $e) { exception_handler($e); } } $this->updateVisibility($modelIds, false); $this->deleteDelayedUpdate($model_list); } } public function updatePhotosProductsRelation($abraProductID, $kupshopProductID) { $apRows = sqlQueryBuilder() ->select('ap.*, ppr.id_photo AS ppr_id_photo, ppr.show_in_lead AS show_in_lead')->from('abra_pictures', 'ap') ->leftJoin('ap', 'photos_products_relation', 'ppr', 'ppr.id_photo = ap.id_picture AND ppr.id_product=:product_id') ->setParameter('product_id', $kupshopProductID) ->where(\Query\Operator::equals(['ap.id_abra_product' => $abraProductID])) ->andWhere('ap.id_picture IS NOT NULL AND ap.type IS NOT NULL') ->execute(); $cotyAbraPictures = []; $lorealAbraPictures = []; $smallPartnersVip = []; $otherAbraPictures = []; $cotyMain = null; $lorealMain = null; $smallPartnersVipMain = null; foreach ($apRows as $ap) { if ($ap['type'] == 21) { $cotyMain = $ap; } if ($ap['type'] == 31) { $lorealMain = $ap; } if ($ap['type'] == 51) { $smallPartnersVipMain = $ap; } if ($ap['type'] >= 21 && $ap['type'] <= 30) { $cotyAbraPictures[] = $ap; } elseif ($ap['type'] >= 31 && $ap['type'] <= 40) { $lorealAbraPictures[] = $ap; } elseif ($ap['type'] >= 51 && $ap['type'] <= 60) { $smallPartnersVip[] = $ap; } else { $otherAbraPictures[] = $ap; } } if (count($cotyAbraPictures) || count($lorealAbraPictures) || count($smallPartnersVip)) { $forcedOtherPictures = []; // other pictures must NOT be displayed when any COTY/loreal/small_partners_vip picture is present $IDs = array_map( function ($val) { return $val['id_picture']; }, array_filter($otherAbraPictures, function ($val) use (&$forcedOtherPictures, &$cotyAbraPictures, &$lorealAbraPictures, &$smallPartnersVip, &$cotyMain, &$lorealMain, &$smallPartnersVipMain ) { if (in_array($val['type'], [5])) { // force displaying shade (type 5) even with COTY/loreal/small_partners_vip pictures present $forcedOtherPictures[] = $val; return false; } elseif ($val['type'] == 2 && ( (count($cotyAbraPictures) && is_null($cotyMain)) || (count($lorealAbraPictures) && is_null($lorealMain)) || (count($smallPartnersVip) && is_null($smallPartnersVipMain)) )) { // force display type 2 when 31/21/51 is missing but other loreal/coty/small_partners_vip pictures are present (#9774) $forcedOtherPictures[] = $val; return false; } return !empty($val['ppr_id_photo']); }) ); if (count($IDs)) { sqlQueryBuilder()->delete('photos_products_relation') ->where(\Query\Operator::inStringArray($IDs, 'id_photo')) ->andWhere(\Query\Operator::equals(['id_product' => $kupshopProductID])) ->execute(); } $toInsert = array_merge($cotyAbraPictures, $lorealAbraPictures, $smallPartnersVip, $forcedOtherPictures); } else { $toInsert = $otherAbraPictures; } foreach ($toInsert as $photo) { $type = static::$PICTURE_TYPES[$photo['type']] ?? static::$PICTURE_TYPES['default'] ?? 'ignore'; if (!empty($photo['ppr_id_photo']) && $photo['show_in_lead'] !== $type && $type !== 'ignore' && $type !== 'nothing') { // for existing relations update show_in_lead if it doesn't match sqlQuery( 'UPDATE photos_products_relation SET show_in_lead = :new_show_in_lead WHERE id_photo=:id_photo AND id_product=:id_product AND show_in_lead=:show_in_lead', ['id_photo' => $photo['id_picture'], 'id_product' => $kupshopProductID, 'show_in_lead' => $photo['show_in_lead'], 'new_show_in_lead' => $type] ); } elseif ($type === 'ignore' || $type == 'nothing' || !empty($photo['ppr_id_photo'])) { continue; } sqlQuery("INSERT IGNORE INTO photos_products_relation (id_photo, id_product, id_variation, show_in_lead, active) VALUES (:id_picture, :id_product , :id_variation, :show_in_lead , 'Y')", ['id_picture' => $photo['id_picture'], 'id_product' => $kupshopProductID, 'show_in_lead' => $type, 'id_variation' => null]); } // coty main photo takes precedence when loreal main photo is also present #9669 $mainPicturesToMakeSecondary = []; if (isset($cotyMain) && (isset($lorealMain) || isset($smallPartnersVipMain))) { if (isset($lorealMain)) { $mainPicturesToMakeSecondary[] = $lorealMain['id_picture']; } if (isset($smallPartnersVipMain)) { $mainPicturesToMakeSecondary[] = $smallPartnersVipMain['id_picture']; } } elseif (isset($lorealMain) && isset($smallPartnersVipMain)) { // otherwise Loreal main photo takes precedence if (isset($smallPartnersVipMain)) { $mainPicturesToMakeSecondary[] = $smallPartnersVipMain['id_picture']; } } if (count($mainPicturesToMakeSecondary)) { sqlQueryBuilder()->update('photos_products_relation') ->set('show_in_lead', "'V'") ->where(\Query\Operator::equals([ 'id_product' => $kupshopProductID, 'id_variation' => null, 'show_in_lead' => 'Y', ])) ->andWhere(\Query\Operator::inStringArray($mainPicturesToMakeSecondary, 'id_photo')) // column_name in ppr is id_photo not id_picture ->execute(); } } public function updateProduct($product, $language = null) { if (empty($product['id_serie']) || empty($product['id_section'])) { // Incomplete product, suspend $this->updateSQL('products', ['id_model' => null], ['id' => $product['id']]); return false; } else { $id_model = returnSQLResult('SELECT am.id_abra FROM abra_models am WHERE '.$this->createWhere([ 'am.id_serie' => $product['id_serie'], 'am.id_section' => $product['id_section'], 'am.id_complement_model' => $product['id_complement_model'], ]).' ORDER BY am.id_abra', [ 'id_serie' => $product['id_serie'], 'id_section' => $product['id_section'], 'id_complement_model' => $product['id_complement_model'], ]); } if (!$id_model) { // No model found $this->updateSQL('products', ['id_model' => null], ['id' => $product['id']]); return false; } // Insert photos $this->updatePhotosProductsRelation($product['code'], $product['id']); // Update product title $flags = explodeFlags($product['campaign']); $campaign = $this->getCampaignsNames( array_filter($flags, function ($k) { return in_array($k, ['V', 'T', 'O', 'S']); }, ARRAY_FILTER_USE_KEY) ); // Update flag SET when X_Constitution is not empty and X_Set is empty #9969 $setFlag = 'SET'; $setValue = !empty($product['set']) && !($flags['S'] ?? false); if ($setValue != (bool) ($flags['SET'] ?? false)) { $setData = ['id_product' => $product['id'], 'flag' => $setFlag]; $this->updateProductFlag($setData, $setValue); } $modelTitle = $this->generateModelTitle($product)['title']; // Doplnek typu produktu + Name2 + velikost + odstín + poškození + (vzorek/odstřik/tester/kazeta) $attributes = [ $product['complement_type_name'], $product['name2'], $product['size'], $product['shade'], $product['damage_name'], join(' ', $campaign), ]; $title = join(' ', array_filter($attributes)); $title = join(' ', [$modelTitle, $title]); if ($product['id_model'] != $id_model) { // Update product photo $this->scheduleProductTitleUpdate($product['id']); $this->scheduleProductTitleUpdate(null, null, null, null, null, $id_model); } $this->checkLeadProductPhoto($product['id']); // update product section $id_sections = $this->getSectionIDs($product['id_section'], $product['sex']); $this->deleteSQL('products_in_sections', ['id_product' => $product['id'], 'deletable' => 1]); foreach ($id_sections as $id_section) { sqlQuery( 'REPLACE INTO products_in_sections (id_product, id_section, deletable) VALUES (:id_product, :id_section, :deletable)', ['id_product' => $product['id'], 'id_section' => $id_section, 'deletable' => 1] ); } // update secondary sections (assortment groups) if (!empty($product['sex'])) { // sex is required to properly assign section $this->deleteSQL('products_in_sections', ['id_product' => $product['id'], 'deletable' => -1]); $secondarySections = sqlQueryBuilder()->select('id_abra_section')->from('abra_secondary_assortment_group') ->where(Operator::equals(['id_product' => $product['id']])) ->andWhere("id_abra_section != '0'")->orderBy('id_abra'); foreach ($secondarySections->execute() as $secondarySection) { $sectionIDs = $this->getSectionIDs($secondarySection['id_abra_section'], $product['sex']); foreach ($sectionIDs as $sectionID) { sqlQuery('INSERT IGNORE INTO products_in_sections SET id_product=:id_product, id_section=:id_section, deletable=:deletable', [ 'id_product' => $product['id'], 'id_section' => $sectionID, 'deletable' => -1, // set lower ordering priority for secondary sections ]); } } } // update category parameter if ($product['id_section']) { if ($valueListID = $this->selectSQL('abra_sections', ['id_abra' => $product['id_section']], ['id_parameters_list'])->fetchColumn()) { $data = ['id_parameter' => self::CATEGORY_PARAMETER_ID, 'id_product' => $product['id']]; $this->deleteSQL('parameters_products', $data); $data['value_list'] = $valueListID; $this->insertSQL('parameters_products', $data); } // update main category parameter if ($sectionId = $this->selectSQL('abra_sections', ['id_abra' => $product['id_section']], ['id_section'])->fetchColumn()) { // check parameter exists if (!$this->selectSQL('parameters', ['id' => self::MAIN_CATEGORY_PARAMETER_ID])->fetch()) { $this->insertSQL('parameters', [ 'id' => self::MAIN_CATEGORY_PARAMETER_ID, 'name' => 'Hlavní kategorie', 'value_type' => 'list', ]); } // find top section if ($section = ServiceContainer::getService(SectionTree::class)->get()->getSectionById($sectionId)) { $topSection = null; // get first section that have record in abra_sections foreach ($section->getParents() as $parent) { if ($this->selectSQL('abra_sections', ['id_section' => $parent->getId()])->fetch()) { $topSection = $parent; break; } } if ($topSection) { if (!($valueListID = $this->selectSQL('parameters_list', ['id_parameter' => self::MAIN_CATEGORY_PARAMETER_ID, 'value' => $topSection->getName()], ['id'])->fetchColumn())) { $this->insertSQL( 'parameters_list', [ 'id_parameter' => self::MAIN_CATEGORY_PARAMETER_ID, 'value' => $topSection->getName(), ] ); $valueListID = sqlInsertId(); } $data = ['id_parameter' => self::MAIN_CATEGORY_PARAMETER_ID, 'id_product' => $product['id']]; $this->deleteSQL('parameters_products', $data); $data['value_list'] = $valueListID; $this->insertSQL('parameters_products', $data); if ($language !== null && ($language != $this->languageContext->getDefaultId())) { if ($sectionTranslatedName = $this->selectSQL('sections_translations', ['id_section' => $topSection->getId(), 'id_language' => $language], ['name'])->fetchColumn()) { $this->saveTranslationObject(ParametersListTranslation::class, ['id_object' => $valueListID, 'id_language' => $language], ['value' => $sectionTranslatedName]); } } } } } } // check for new product if (empty($product['id_model']) && $id_model && $product['newly_added']) { $productObj = new Product(); $productObj->createFromDB($product['id']); $eventDispatcher = ServiceContainer::getService('event_dispatcher'); $event = new ProductEvent($productObj); $eventDispatcher->dispatch($event, ProductEvent::PRODUCT_CREATED); // remove newly_added from product sqlQueryBuilder()->update('products') ->set('newly_added', 0) ->where(Operator::equals(['id' => $product['id']])) ->execute(); } $updateTitle = true; // neaktualizovat title u darku - aktualizovat ho pouze jednou pri vytvoreni if (isset($flags['G']) && !empty($product['title']) && $product['title'] != $product['code']) { $updateTitle = false; } if ($language === null || ($language == $this->languageContext->getDefaultId())) { $update = ['id_model' => $id_model, 'id_serie' => $product['id_serie_kupshop']]; if ($updateTitle) { $update['title'] = $title; } if (!empty($product['id_producer'])) { $update['producer'] = $product['id_producer']; } $this->updateSQL('products', $update, ['id' => $product['id']]); } else { if ($updateTitle) { $this->saveTranslationObject(ProductsTranslation::class, ['id_object' => $product['id'], 'id_language' => $language], ['title' => $title]); } } return true; } public function hideIncompleteProducts() { // Skryt vyrobce bez viditelnych produktu $SQL = sqlQuery('UPDATE producers pr SET active = IF((SELECT COUNT(*) FROM products WHERE figure = "Y" AND producer = pr.id) > 0, "Y", "N")'); if (sqlAffectedRows($SQL) > 0) { var_dump(['Skryto/Zobrazeno výrobců s/bez viditelných produktů', sqlAffectedRows($SQL)]); } } public static $sections; public function sync_section($code, $field, $value) { $ids = $this->getMappingFromAbraMultiple('section', $code); if (!$ids) { // Create Sections $vat_id = null; getVat($vat_id); $parents = [1, 2, 4]; // Do bytu if ($code == 'EL00000101' || $code == 'DL00000101') { $parents = [3]; } foreach ($parents as $parent) { $this->insertSQL('sections', [ 'name' => $value, 'behaviour' => 2, 'figure' => 'Y', 'orderby' => 'sell', 'orderdir' => 'DESC', ]); $id = sqlInsertId(); if (!$this->selectSQL('sections', ['id' => $parent])->fetch()) { $parent = null; } $this->insertSQL('sections_relation', ['id_section' => $id, 'id_topsection' => $parent, 'position' => 999]); $this->setMappingFromAbra('section', $id, $code); $ids[] = $id; } } $column = static::$SECTION_FIELDS[$field]; if ($this->isTranslationsSync()) { if (!$this->translationFieldSupported($field)) { return; } switch ($column) { case 'descr': $blocks = ServiceContainer::getService(\KupShop\I18nBundle\Translations\BlocksTranslation::class); foreach ($ids as $id) { $idRootBlock = $this->selectSQL('sections', ['id' => $id], ['id_block'])->fetchColumn(); if ($idRootBlock) { $objectID = $this->selectSQL('blocks', ['id_parent' => $idRootBlock, 'id_root' => $idRootBlock], ['id'])->fetchColumn(); } else { // create empty block $service = $this->getBlockUtil(); $objectID = $service->insertFirstBlock('sections', $id, ''); } if ($objectID) { $blocks->saveSingleObject($this->lang, $objectID, ['content' => $value]); } } break; case 'name': foreach ($ids as $id) { $this->saveTranslationObject(SectionsTranslation::class, ['id_object' => $id], ['name' => $value]); if ($valueListID = $this->selectSQL('abra_sections', ['id_abra' => $code], ['id_parameters_list'])->fetchColumn()) { $this->saveTranslationObject(ParametersListTranslation::class, ['id_object' => $valueListID], ['value' => $value]); } } break; default: foreach ($ids as $id) { $this->saveTranslationObject(SectionsTranslation::class, ['id_object' => $id], [$column => $value]); } break; } foreach ($ids as $index => $id) { $this->scheduleProductTitleUpdate(null, null, null, null, $id); } return; } if (!static::$sections) { static::$sections = ServiceContainer::getService(SectionTree::class); } switch ($column) { case 'parent_id': $parent_ids = $this->getMappingFromAbraMultiple('section', $value); if (!$parent_ids) { if ($value > 0) { echo 'Unknown section ID: '.$value; } // throw new Exception('Unknown section ID: '.$value); break; } // Do bytu if ($value == 'EL00000101' || $code == 'DL00000101') { $parent_ids = [3]; } foreach ($ids as $index => $id) { if (empty($parent_ids[$index])) { $this->deleteSQL('sections', ['id' => $id]); continue; } $this->updateSQL('sections_relation', ['id_section' => $id, 'id_topsection' => $parent_ids[$index]], ['id_section' => $id]); } return; case 'position': sqlQuery('UPDATE sections_relation SET position=:position WHERE id_section IN (:ids)', ['ids' => $ids, 'position' => $value], ['ids' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]); return; case 'plural': if (empty($value)) { return; } foreach ($ids as $index => $id) { $section = static::$sections->getSectionById($id); $map = [ '1' => 'pro ženy', '2' => 'pro muže', // '3' => "do bytu", '4' => 'pro děti', ]; if (empty($section['parents'])) { continue; } $index = key($section['parents']); $plural = $value; if (!empty($map[$index])) { $plural .= ' '.translate($map[$index], 'sections'); } $this->updateSQL('sections', ['plural' => $plural], ['id' => $id]); } break; case 'ignore': return; case 'descr': $service = $this->getBlockUtil(); foreach ($ids as $index => $id) { $service->insertFirstBlock('sections', $id, $value); } return; case 'figure': $value = $this->parseBool($value); $value = $value ? 'Y' : 'N'; foreach ($ids as $index => $id) { $this->updateSQL('sections', [$column => $value], ['id' => $id]); } break; case 'delete': foreach ($ids as $index => $id) { $this->deleteSQL('sections', ['id' => $id]); } return; case 'name': // check if category parameter exists if (!$this->selectSQL('parameters', ['id' => self::CATEGORY_PARAMETER_ID])->fetch()) { $this->insertSQL('parameters', [ 'id' => self::CATEGORY_PARAMETER_ID, 'value_type' => 'list', 'position' => 0, ]); } foreach ($ids as $index => $id) { $this->updateSQL('sections', ['name' => $value], ['id' => $id]); // update category parameter if (!empty($value)) { // je nesmysl ukladat prazdnou hodnotu if (!($valueListID = $this->selectSQL('abra_sections', ['id_abra' => $code], ['id_parameters_list'])->fetchColumn())) { if (!($valueListID = $this->selectSQL('parameters_list', ['id_parameter' => self::CATEGORY_PARAMETER_ID, 'value' => $value], ['id'])->fetchColumn())) { $this->insertSQL( 'parameters_list', [ 'id_parameter' => self::CATEGORY_PARAMETER_ID, 'value' => $value, ] ); $valueListID = sqlInsertId(); } $this->updateSQL('abra_sections', ['id_parameters_list' => $valueListID], ['id_abra' => $code]); } else { $this->updateSQL('parameters_list', ['value' => $value], ['id' => $valueListID]); } } } break; case 'replacement': $ids_new = $this->getMappingFromAbraMultiple('section', $value); if ($ids && $ids_new) { foreach ($ids as $key => $id) { $id_new = $ids_new[$key] ?? null; if ($id_new) { $this->createRewrite( \KupShop\ElninoBundle\Util\Rewrite::TYPE_CATEGORY, $id_new, [ 's' => 'category', 'IDcat' => $id, ] ); } } } break; case null: var_dump('Section: Nedefinovaná položka synchronizace: '.$field); return; default: foreach ($ids as $index => $id) { $this->updateSQL('sections', [$column => $value], ['id' => $id]); } } foreach ($ids as $index => $id) { $this->scheduleProductTitleUpdate(null, null, null, null, $id); } } public function getSectionIDs($abra_id, $sex) { if (!$abra_id) { return null; } $sections = sqlFetchAll(sqlQuery('SELECT abs.id_section AS id, sr.id_topsection FROM abra_sections abs LEFT JOIN sections_relation sr ON sr.id_section = abs.id_section WHERE id_abra=:code ORDER BY abs.id_section', ['code' => $abra_id] )); if (empty($sections)) { var_dump('unknown section: '.$abra_id); return null; } // Return "do bytu" foreach ($sections as $section) { if ($section['id_topsection'] == '3') { return [$section['id']]; } } $return = null; if ($sex == 'W') { $return = [$sections[0]]; } elseif ($sex == 'M') { if (count($sections) < 2) { throw new Exception('Malo sekci?'); } $return = [$sections[1]]; } elseif ($sex == 'K') { if (count($sections) > 2) { $return = [$sections[2]]; } elseif (count($sections) == 2) { $this->insertSQL('sections', ['name' => $sections['id'], 'behaviour' => 2, 'figure' => 'Y']); $this->setMappingFromAbra('section', sqlInsertId(), $abra_id); } else { throw new Exception('Malo sekci?'); } } elseif ($sex == 'U' || $sex == '' || $sex == 'B') { if (count($sections) < 2) { throw new Exception('Malo sekci?'); } $return = array_slice($sections, 0, 2); } else { throw new Exception('wtf??'); } return array_map(function ($x) { return $x['id']; }, $return); } /************************* ORDERS *****************************/ public function syncOrder($id) { // Ignore order for now if sync failed $cacheId = 'order-sync-failed-'.$id; if (getCache($cacheId)) { return; } try { AbraBase::syncOrder($id); } catch (RuntimeException $e) { // Wait 2 minutes before sync retry setCache($cacheId, $id, 120); throw $e; } // Pokud by se stala chyba, vylitla by exception. Tady vím, že je to OK $order = new Order(); $order->createFromDB($id); $order->logHistory(translate('abra_order_send', 'abra')); $order->clearModified(); } public function getOrder($id) { $order = new Order(); $order->createFromDB($id, true, true, true); return $this->contextManager->activateOrder($order, function () use ($order) { $delivery = null; $payment = null; $department = null; $forcedDeliveryAdName = null; $CH_Recipient = null; // CH_Recipient -> X_sRecipient if (findModule('eshop_delivery')) { $delivery_type = $order->getDeliveryType($order->getDeliveryId()); $delivery = $this->getMappingToAbra('delivery', $delivery_type->id_delivery) ?: null; $payment = $this->getMappingToAbra('payment', $delivery_type->id_payment) ?: null; if ($order->total_price->isZero()) { $payment = 'DP'; } $deliveryClass = $delivery_type->getDelivery(); if ($deliveryClass instanceof BalikDoRuky) { $department = 'A'; } elseif ($deliveryClass instanceof Zasilkovna) { $department = $deliveryClass->getInfo()['labelRouting']; } elseif ($deliveryClass instanceof PPLParcelShop) { $deliveryInfo = $deliveryClass->getInfo(); $department = $deliveryInfo['code'] ?? $deliveryInfo['id']; $forcedDeliveryAdName = $deliveryInfo['name']; $CH_Recipient = 'Parcelshop '.$deliveryInfo['id']; // #9769 } elseif ($deliveryClass instanceof SPSParcelShop) { $deliveryInfo = $deliveryClass->getInfo(); $department = $deliveryInfo['id']; $forcedDeliveryAdName = $deliveryInfo['name']; $CH_Recipient = 'Parcelshop '.$deliveryInfo['id']; // #9769 } $paymentClass = $delivery_type->getPayment(); if ($paymentClass->getPayMethod() == \Payment::METHOD_COD) { $currencyContext = \KupShop\KupShopBundle\Util\Contexts::get(\KupShop\KupShopBundle\Context\CurrencyContext::class); $order->total_price = roundPrice($order->total_price, currency: $currencyContext->getOrDefault($order->getCurrency())); } } $user_abra_id = getVal('elnino_abra_id', sqlFetchAssoc($this->selectSQL('users', ['id' => $order->id_user], ['elnino_abra_id'])), 519194); if ($this->settings['web'] == 'perfumy-hurtownia.pl') { $order->total_price = roundPrice($order->total_price); } $sync_order = [ // Header 'WEBsource' => $this->settings['web'], 'DocumentNumber' => $order->order_no, 'WEBnote' => $this->prepareTextForOrder($order->note_user), 'Currency' => $order->currency, 'PaymentCode' => $payment, 'TransportCode' => $delivery, 'Amount' => $this->preparePriceToAbra($order->total_price), 'Department' => $department, 'Points' => null, 'SMS' => 'A', 'DocumentID' => $order->id, 'Status' => 0, 'CustomerServiceNote' => null, // Customer 'Code' => $user_abra_id, 'Email' => $this->prepareFieldLengthForOrder($order->invoice_email, 100), 'PhoneNumber' => $this->prepareFieldLengthForOrder(!empty($order->delivery_phone) ? $order->delivery_phone : $order->invoice_phone, 30), // Delivery address 'adName' => $this->prepareFieldLengthForOrder(!empty($forcedDeliveryAdName) ? $forcedDeliveryAdName : (!empty($order->delivery_firm) ? $order->delivery_firm : $order->delivery_custom_address), 50), 'adFirstName' => $this->prepareFieldLengthForOrder($order->delivery_name, 50), 'adLastName' => $this->prepareFieldLengthForOrder($order->delivery_surname, 50), 'adStreet' => $this->prepareFieldLengthForOrder($order->delivery_street, 50), 'adCity' => $this->prepareFieldLengthForOrder($order->delivery_city, 50), 'adPostCode' => $this->prepareFieldLengthForOrder($order->delivery_zip, 10), 'adCountryCode' => $this->prepareFieldLengthForOrder($order->invoice_country, 3), // Invoice address 'amName' => $this->prepareFieldLengthForOrder(!empty($order->invoice_firm) ? $order->invoice_firm : $order->invoice_custom_address, 50), 'amFirstName' => $this->prepareFieldLengthForOrder($order->invoice_name, 50), 'amLastName' => $this->prepareFieldLengthForOrder($order->invoice_surname, 50), 'amStreet' => $this->prepareFieldLengthForOrder($order->invoice_street, 50), 'amCity' => $this->prepareFieldLengthForOrder($order->invoice_city, 50), 'amPostCode' => $this->prepareFieldLengthForOrder($order->invoice_zip, 10), 'OrgIdentNumber' => $this->prepareFieldLengthForOrder($order->invoice_ico, 15), 'VATIdentNumber' => $this->prepareFieldLengthForOrder($order->invoice_dic, 20), 'CH_Kanton' => null, 'CH_Recipient' => $this->prepareFieldLengthForOrder(!empty($CH_Recipient) ? $CH_Recipient : $order->invoice_state, 60), 'Rows' => $this->getOrderRows($order), ]; if ($order->id_language != $this->languageContext->getDefaultId()) { $sync_order['CH_Kanton'] = $order->id_language; } if ($order->hasSameAddress()) { $empty_af = [ 'amName' => null, 'amFirstName' => null, 'amLastName' => null, 'amStreet' => null, 'amCity' => null, 'amPostCode' => null, ]; $sync_order = array_merge($sync_order, $empty_af); } return $sync_order; }); } public function sync_order($code, $field, $value, $value2) { if (!isset(static::$ORDER_FIELDS[$field])) { var_dump('Nedefinovaná položka synchronizace:', $field, $value, $value2); return; } $column = static::$ORDER_FIELDS[$field]; $product_code = null; if ($column == 'quantity' || $column == 'price') { [$code, $product_code] = explode('~', $code); } $id = $this->getMappingFromAbra('order', $code); if (!$id) { var_dump("Neznámá objednávka: {$code}"); return false; } $value = trim($value); $value2 = trim($value2); $order = new Order(); $order->createFromDB($id); return $this->sync_order_switch($value, $column, $order, $id, $product_code, $value2); } public function getPackageSentMessage(Delivery $delivery) { foreach ($this->package_sent_messages as $delivery_class => $message) { if ($delivery instanceof $delivery_class) { return $message; } } return null; throw new Exception('Nezmámý způsbob doručení balíku: '.$delivery->id); } protected function sync_order_switch($value, $column, Order $order, $id, $product_code, $value2 = null) { switch ($column) { case 'status': if ($value == 6) { $order->setData('storno_from_abra', true); $order->storno(false, 'orderAbraCancelled'); return true; } // Package Sent if ($value == 10) { if (!$order->package_id) { $order->logHistory('Missing package number!'); } $SQL = sqlQueryBuilder()->select('id')->from('orders_history') ->where(Operator::equals(['id_order' => $order->id, 'notified' => 1, 'id_status' => 10])); if ($SQL->execute()->rowCount() > 0) { // already sent $order->changeStatus($value); return true; } if ($message = $this->getPackageSentMessage($order->getDeliveryType()->getDelivery())) { $order->changeStatus($value, null, true, $message); return true; } return false; } // Ignore statuses > 7 if ($value > 7) { return false; } // Ignore status "new" if ($value <= 0) { return false; } $msg = translate('abra_order_status_change', 'abra').' '.$value2; // Merge statuses with message $history = $order->getHistory(true); $lastMessage = end($history); if ($value == 2) { if (strpos($lastMessage['comment'], translate('abra_order_error', 'abra')) !== false) { $this->deleteSQL('orders_history', ['id' => $lastMessage['id']]); $msg = $lastMessage['comment']; } } elseif ($lastMessage['id_status'] == $value) { return false; } // Disable status change from done/cancel if ($order->status >= 6 && $value < 6) { $value = $order->status; } $order->changeStatus($value, $msg, false); break; case 'note_expedition': $noteExpedition = translate('abra_order_error', 'abra'); $order->logHistory("{$noteExpedition}:
{$value}"); break; case 'note_service': // $order->logHistory("Problém opraven:
{$value}"); break; case 'package_number': $orderData = $order->getDataAll(true); $orderData['packages_elnino'] = $orderData['packages_elnino'] ?? []; $assignToPackageSource = function ($packageID) use (&$orderData) { if (str_starts_with($packageID, 'ZNZ_')) { $orderData['packages_elnino']['znz'] = $packageID; } else { $orderData['packages_elnino']['elnino'] = $packageID; } }; if (empty($orderData['packages_elnino']) && !empty($orderData['packages'])) { // legacy fallback foreach ($orderData['packages'] as $packageID) { $assignToPackageSource($packageID); } } $orderData['packages'][$value] = $value; $assignToPackageSource($value); $order->setDataAll($orderData); $removePrefix = function ($str) { $prefix = 'ZNZ_'; if (str_starts_with($str, $prefix)) { $str = substr($str, strlen($prefix)); } return $str; }; $packageIDs = join(',', array_map($removePrefix, array_values($orderData['packages_elnino']))); $this->updateSQL('orders', ['package_id' => $packageIDs], ['id' => $id]); $order->package_id = $packageIDs; $order->logHistory("Číslo balíku: {$value}"); break; case 'quantity': $data = ['id_order' => $id, 'product_code' => $product_code]; $items = sqlQuery( 'SELECT oi.id FROM order_items oi JOIN products_variations pv ON pv.id = oi.id_variation WHERE id_order=:id_order AND pv.code=:product_code', $data ); if (sqlNumRows($items) != 1) { return; } // logError(__FILE__, __LINE__, "Nelze najít změněnou položku objednávky: ".print_r($data, true)); $item_id = sqlFetchAssoc($items)['id']; $order->updateItem($item_id, $value); break; case 'note_storno': $order->logHistory("Důvod stornování objednávky:
{$value}"); break; case 'ExternalOrder': $order->setData('Abra_ExternalOrder', $value); break; case 'ignore': break; default: throw new Exception('Nedefinovany sloupec: '.$column); } } public function sync_producer($code, $field, $value) { if ((static::$PRODUCER_FIELDS[$field] ?? false) === 'id_richcontent') { $id = $this->getMappingFromAbra('producer', $code); return $this->richContentUtil->handleSync('producers', $id, $value, $this->lang); } else { return parent::sync_producer($code, $field, $value); } } public function sync_serie($code, $field, $value) { if ((static::$SERIES_FIELDS[$field] ?? false) === 'id_richcontent') { $id = $this->getMappingFromAbra('serie', $code); return $this->richContentUtil->handleSync('abra_series', $id, $value, $this->lang); } else { return parent::sync_serie($code, $field, $value); } } public function updateOrder($id, $status, $message) { sqlGetConnection()->transactional(function () use ($id, $status, $message) { sqlQuery('SELECT COUNT(*) FROM abra_orders FOR UPDATE'); $sync_order = $this->getOrder($id); $sync_order['CustomerServiceNote'] = $this->prepareFieldLengthForOrder($this->prepareTextForOrder($message), 200); $sync_order['Status'] = $status; $order = new Order(); $order->createFromDB($id); $newOrderAbraId = false; $this->log(['Update order initiated', 'id' => $id, 'order' => $sync_order]); try { if ($order->isHardModified()) { $this->log(['Update order full', 'id' => $id, 'order' => $sync_order]); $newOrderAbraId = true; $ret = $this->client->__call('weSetReceivedOrder', $sync_order); } else { // soft modification $this->log(['Update order soft', 'id' => $id, 'order' => $sync_order]); $allowedFields = [ 'WEBnote', 'CustomerServiceNote', // 'Department', 'Email', 'PhoneNumber', 'adName', 'adFirstName', 'adLastName', 'adStreet', 'adCity', 'adPostCode', 'amName', 'amFirstName', 'amLastName', 'amStreet', 'amCity', 'amPostCode', 'OrgIdentNumber', 'Code', // 'Status', 'CH_Recipient', // provincie pro parfimo.it ]; if ($status >= 0) { $allowedFields[] = 'Status'; } $finalFields = [ 'WEBsource' => $sync_order['WEBsource'], 'DocumentNumber' => $sync_order['DocumentNumber'], 'Rows' => [], ]; foreach ($allowedFields as $allowedField) { $finalFields['Rows'][$allowedField] = $allowedField.'~|~'.$sync_order[$allowedField]; } $this->log(['Update order partial', 'fields' => $finalFields]); $ret = $this->client->__call('weSetReceivedOrderSimpleUpdate', $finalFields); } } catch (Exception $e) { $this->log(['Update order', 'exception' => $e->getMessage()]); $this->printTrace(); throw new AbraUpdateOrderException($e->getMessage(), $e->getCode(), $e); } if ($ret[0] == 'True') { $this->log(['Update order' => 'success', 'id' => $id, 'abra_id' => $ret[2]]); if ($newOrderAbraId) { $this->deleteSQL('abra_orders', ['id_order' => $id]); $this->setMappingFromAbra('order', $id, $ret[2]); } } else { $this->log(['Update order' => 'error', 'id' => $id, 'error' => $ret[1]]); $this->printTrace(); throw new AbraUpdateOrderException("Order {$id} sync failed: {$ret[1]} - {$ret[2]}"); } $order = new Order(); $order->createFromDB($id); $order->clearModified(); }); } public function updateOrderStatuses() { // Authorize orders sent to Abra with online payment in status New $this->authorizeOrders(function (Query\QueryBuilder $qb) { $qb->andWhere("o.status = 0 AND (o.note_user = '' OR o.note_user IS NULL)") ->andWhere(\Query\Order::byPaymentMethods(['METHOD_COD'])); }, translate_shop('order_authorized', 'abra')); // Authorize orders sent to Abra with online payment in status New $this->authorizeOrders(function (Query\QueryBuilder $qb) { $qb->andWhere("o.status = 0 AND o.status_payed = 1 AND (o.note_user = '' OR o.note_user IS NULL)") ->andWhere(\Query\Order::byPaymentMethods(['METHOD_ONLINE'])); }, translate_shop('order_paid_authorized', 'abra')); } public function authorizeOrders($spec, $message) { $orders = sqlQueryBuilder()->select('o.id, o.modified')->from('orders', 'o') ->join('o', 'abra_orders', 'ao', 'ao.id_order = o.id') ->join('o', 'delivery_type', 'dt', 'dt.id = o.id_delivery') ->andWhere(\Query\Operator::not(\Query\Operator::findInSet(['D'], 'o.flags'))) ->andWhere($spec)->execute(); foreach ($orders as $order) { $this->log(['AbraKoza2 authorizeOrders', 'id' => $order['id'], 'order' => $order]); if ($order['modified']) { $this->updateOrder($order['id'], 1, translate_shop('order_fully_authorized', 'abra')); $order_obj = new Order($order['id']); $order_obj->createFromDB($order['id']); $order_obj->changeStatus(1, translate_shop('order_fully_authorized', 'abra'), false); } else { $this->changeOrderStatus($order['id'], 1, $message); } } } public function changeOrderStatus($id_order, $status, $comment, $service_note = null, $emailType = null) { try { $order = new Order(); $order->createFromDB($id_order); $this->log(['Change order status', 'id' => $id_order, 'code' => $order->order_no, 'status' => $status, 'comment' => $comment]); // nevolat storno do abry pokud uz byla objednavka stornovana z abry if ($order->getData('storno_from_abra') !== true) { $ret = $this->client->__call('weSetAttribute2', [$this->settings['web'], 'ReceivedOrders', 'ExternalNumber', $order->order_no, 'X_Status', $status]); if ($service_note) { $service_note = $this->prepareFieldLengthForOrder($this->prepareTextForOrder($service_note), 200); $this->client->__call('weSetAttribute2', [$this->settings['web'], 'ReceivedOrders', 'ExternalNumber', $order->order_no, 'X_NoteCUSTOMERSERVICE', $service_note]); } if ($ret[0] != 'True') { throw new Exception("{$ret[1]} - {$ret[2]}"); } } $order->changeStatus($status, $comment, isset($emailType), null, $emailType); } catch (Exception $e) { $this->log(['Change order status', 'id' => $id_order, 'exception' => $e->getMessage()]); throw $e; } } public function preparePriceFromEUR($value) { return $this->preparePriceFromAbra($value); } protected function computeVariationPrice($id) { } public function checkLeadProductPhoto($id) { if (static::$pictureType6AsLead) { // UPDATE type 6 to 'Y' and corresponding type 2 to 'V' (original type 6 show_in_lead) sqlQuery(" UPDATE photos_products_relation ppr_type2 INNER JOIN photos_products_relation ppr_type6 ON ppr_type6.id_product=ppr_type2.id_product AND ppr_type6.show_in_lead != 'Y' INNER JOIN abra_pictures ap_type6 ON ap_type6.id_picture=ppr_type6.id_photo AND ap_type6.type = 6 SET ppr_type2.show_in_lead = ppr_type6.show_in_lead, ppr_type6.show_in_lead = ppr_type2.show_in_lead WHERE ppr_type2.id_product=:id_product AND ppr_type2.show_in_lead = 'Y'; ", ['id_product' => $id]); } // check & update RefreshModelLeadPhotosScript // set main MODEL photo $photo = sqlFetchAssoc(sqlQuery("SELECT ppr.*, p.id_model FROM products p JOIN products mp ON mp.id_model=p.id_model JOIN photos_products_relation ppr ON mp.id = ppr.id_product WHERE p.id=:id_product AND mp.figure='Y' AND ppr.show_in_lead = 'Y' ORDER BY mp.in_store > 0 DESC, FIND_IN_SET('T', mp.campaign), FIND_IN_SET('V', mp.campaign), FIND_IN_SET('O', mp.campaign), FIND_IN_SET('P', mp.campaign), FIND_IN_SET('S', mp.campaign), mp.id_complement_type IS NULL DESC, mp.id_complement_type='3D40000101', mp.size_value DESC, mp.price DESC LIMIT 1", ['id_product' => $id])); if ($photo && !empty($photo['id_model'])) { $this->updateSQL('abra_models', ['id_photo' => $photo['id_photo']], ['id_abra' => $photo['id_model']]); } } protected function updateFeedPricesByContentAPI($productIds) { $api = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService(\KupShop\ElninoBundle\Util\ContentAPI\GoogleApi::class); if (!$api->isEnabled()) { return false; } $filterParams = new FilterParams(); $items = sqlQueryBuilder()->select('p.id, p.price, p.in_store, v.vat, p.discount, p.vat as vat_id') ->from('products', 'p') ->join('p', 'vats', 'v', 'v.id = p.vat') ->where(Operator::inIntArray($productIds, 'p.id')) ->andWhere($filterParams->getSpec()) ->groupBy('p.id') ->execute()->fetchAll(); foreach ($items as $item) { $api->addEntry($item); } $api->sendBatch(); } protected function refreshSupplierInStore(array $stores, array $activeStore, int $idProduct): void { if (!findModule(\Modules::PRODUCTS_SUPPLIERS)) { return; // skip products_of_suppliers refresh without products_suppliers module } if (!empty($activeStore['external_supplier'])) { $supplierInStore = $stores[$activeStore['store']] ?? 0; } else { $supplierInStore = 0; } $supplierProductIDArr = [ 'id_product' => $idProduct, 'id_variation' => null, 'id_supplier' => static::$EXTERNAL_SUPPLIER_ID, 'code' => null, ]; $supplierProduct = sqlQueryBuilder()->select('id')->from('products_of_suppliers') ->where(Operator::equalsNullable($supplierProductIDArr)) ->setMaxResults(1)->execute()->fetchAssociative(); if ($supplierProduct) { $this->updateSQL('products_of_suppliers', ['in_store' => $supplierInStore], ['id' => $supplierProduct['id']]); } elseif ($supplierInStore > 0) { $supplierProductIDArr['in_store'] = $supplierInStore; $this->insertSQL('products_of_suppliers', $supplierProductIDArr); } } protected function refreshPartnerFlag(array $activeStore, array $product): void { $campaign = null; if (empty($activeStore['external_supplier']) && $product['is_partner'] >= 1) { $campaign = "REMOVE_FROM_SET('PARTNER', campaign)"; } elseif (static::isExternalPartnerSupplierEnabled() && ($activeStore['external_supplier'] ?? false) && $product['is_partner'] <= 0) { $campaign = "ADD_TO_SET('PARTNER', campaign)"; } if (!empty($campaign)) { sqlQueryBuilder()->update('products')->set('campaign', $campaign) ->where(Operator::equals(['id' => $product['id']])) ->execute(); } } public static function isExternalPartnerSupplierEnabled(): bool { static $enabled = null; if (is_null($enabled)) { $enabled = false; foreach (static::$STORES_HIERARCHY as $store) { if (!empty($store['external_supplier'])) { $enabled = true; break; } } } return $enabled; } } class AbraUpdateOrderException extends Exception { }