Files
masimmo.pl/import-drewmax.php
2026-01-14 10:32:31 +01:00

927 lines
33 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// https://pacyga.pl/wp-content/uploads/woo-feed/custom/xml/komplet-produktow-3.xml
// Include PrestaShop configuration
include(dirname(__FILE__).'/config/config.inc.php');
include(dirname(__FILE__).'/init.php');
$context = Context::getContext();
// Sprawdzenie trybu działania na podstawie parametrów URL
$modeAdd = (Tools::getValue('add') === 'true');
$modeUpdate = (Tools::getValue('update') === 'true');
$modeSynch = (Tools::getValue('synch') === 'true');
if (!$modeAdd && !$modeUpdate && !$modeSynch) {
die('Brak akcji. Dodaj do adresu ?add=true lub ?update=true lub ?synch=true');
}
// Plik logu aktualizacji cen
$logFile = __DIR__ . '/update_price_log.csv';
// Wczytanie XML
$xmlUrl = 'https://pacyga.pl/wp-content/uploads/woo-feed/custom/xml/komplet-produktow-3.xml';
$xml = simplexml_load_file($xmlUrl) or die("Error: Cannot create object");
// === FUNKCJE POMOCNICZE ===
// Function to find attribute group by name
function findAttributeGroupByName($name) {
$id_lang = Context::getContext()->language->id;
$sql = 'SELECT `id_attribute_group`
FROM `'._DB_PREFIX_.'attribute_group_lang`
WHERE `name` = \''.pSQL($name).'\'
AND `id_lang` = '.(int)$id_lang;
$result = Db::getInstance()->getRow($sql);
return $result ? new AttributeGroup($result['id_attribute_group']) : false;
}
// Function to find attribute by name
function findAttributeByName($id_attribute_group, $name) {
$id_lang = Context::getContext()->language->id;
$sql = 'SELECT a.`id_attribute`
FROM `'._DB_PREFIX_.'attribute` a
JOIN `'._DB_PREFIX_.'attribute_lang` al
ON a.`id_attribute` = al.`id_attribute`
WHERE al.`name` = \''.pSQL($name).'\'
AND al.`id_lang` = '.(int)$id_lang.'
AND a.`id_attribute_group` = '.(int)$id_attribute_group;
$result = Db::getInstance()->getRow($sql);
return $result ? new Attribute($result['id_attribute']) : false;
}
// Function to create attribute if it doesn't exist
function createAttribute($name, $values) {
$attributeGroup = findAttributeGroupByName($name);
if (!$attributeGroup) {
$attributeGroup = new AttributeGroup();
$attributeGroup->name = createMultiLangField($name);
$attributeGroup->public_name = createMultiLangField($name);
$attributeGroup->group_type = 'select';
$attributeGroup->add();
}
foreach ($values as $value) {
$attribute = findAttributeByName($attributeGroup->id, $value);
if (!$attribute) {
$attribute = new Attribute();
$attribute->id_attribute_group = $attributeGroup->id;
$attribute->name = createMultiLangField($value);
$attribute->add();
}
}
return $attributeGroup->id;
}
// Helper function to create a multi-language field
function createMultiLangField($field) {
$languages = Language::getLanguages(false);
$res = [];
foreach ($languages as $lang) {
$res[(int)$lang['id_lang']] = $field;
}
return $res;
}
// Helper function to create a valid link_rewrite
function createLinkRewrite($field) {
$languages = Language::getLanguages(false);
$res = [];
$linkRewrite = Tools::link_rewrite($field); // PrestaShop's function to create valid link_rewrite
foreach ($languages as $lang) {
$res[(int)$lang['id_lang']] = $linkRewrite;
}
return $res;
}
// Function to get category ID from name (nieużywana, ale poprawiona)
function getCategoryId($categoryName) {
$result = Category::searchByName(1, $categoryName); // 1 for default language id
if (!empty($result) && isset($result[0]['id_category'])) {
return (int)$result[0]['id_category'];
} else {
// Create category if not exists
$category = new Category();
$category->name = createMultiLangField($categoryName);
$category->link_rewrite = createLinkRewrite($categoryName);
$category->id_parent = 2; // Default parent category
$category->add();
return (int)$category->id;
}
}
function getAllImageUrlsFromXmlProduct($productData, $max = 10)
{
$urls = [];
// główne <image>
if (!empty($productData->image)) {
$urls[] = trim((string)$productData->image);
}
// images_1..10 oraz Images_1..10 (różna wielkość liter w XML!)
for ($i = 1; $i <= $max; $i++) {
$k1 = 'images_' . $i;
$k2 = 'Images_' . $i;
$u1 = isset($productData->{$k1}) ? trim((string)$productData->{$k1}) : '';
$u2 = isset($productData->{$k2}) ? trim((string)$productData->{$k2}) : '';
if ($u1 !== '') $urls[] = $u1;
if ($u2 !== '') $urls[] = $u2;
}
// usuń duplikaty + puste
$urls = array_values(array_unique(array_filter($urls)));
return $urls;
}
// Function to download image from URL and associate it with a product
function addProductImage($productId, $imageUrl, $isCover = false)
{
$image = new Image();
$image->id_product = (int)$productId;
$image->position = Image::getHighestPosition($productId) + 1;
$image->cover = (bool)$isCover; // tylko pierwsze jako cover
$image->add();
$imagePath = $image->getPathForCreation();
// pobranie pliku
$url = str_replace(' ', '%20', trim($imageUrl));
// UWAGA: copy() czasem failuje na HTTPS; file_get_contents + file_put_contents bywa pewniejsze
$data = @file_get_contents($url);
if ($data === false) {
$image->delete();
return false;
}
if (@file_put_contents($imagePath . '.jpg', $data) === false) {
$image->delete();
return false;
}
$imageTypes = ImageType::getImagesTypes('products');
foreach ($imageTypes as $imageType) {
ImageManager::resize(
$imagePath . '.jpg',
$imagePath . '-' . stripslashes($imageType['name']) . '.jpg',
(int)$imageType['width'],
(int)$imageType['height']
);
}
return true;
}
// Function to find product by reference
function findProductByReference($reference) {
$sql = 'SELECT `id_product`
FROM `'._DB_PREFIX_.'product`
WHERE `reference` = \''.pSQL($reference).'\'';
$result = Db::getInstance()->getRow($sql);
return $result ? new Product((int)$result['id_product']) : false;
}
// Function to find combination by product ID and attribute IDs
function findCombinationByAttributes($id_product, $attributeIds) {
if (empty($attributeIds)) {
return false;
}
sort($attributeIds);
$conditions = [];
foreach ($attributeIds as $id_attr) {
$conditions[] = 'pac.`id_attribute` = '.(int)$id_attr;
}
$sql = 'SELECT c.`id_product_attribute`
FROM `'._DB_PREFIX_.'product_attribute` c
JOIN `'._DB_PREFIX_.'product_attribute_combination` pac
ON c.`id_product_attribute` = pac.`id_product_attribute`
WHERE c.`id_product` = '.(int)$id_product.'
GROUP BY c.`id_product_attribute`
HAVING SUM('.implode(' OR ', $conditions).') = '.count($attributeIds);
$result = Db::getInstance()->getRow($sql);
return $result ? new Combination((int)$result['id_product_attribute']) : false;
}
// znajdowanie kombinacji po indeksie/SKU (reference)
function findCombinationByReference($id_product, $reference) {
$sql = 'SELECT `id_product_attribute`
FROM `'._DB_PREFIX_.'product_attribute`
WHERE `id_product` = '.(int)$id_product.'
AND `reference` = \''.pSQL($reference).'\'';
$id = Db::getInstance()->getValue($sql);
return $id ? new Combination((int)$id) : false;
}
function parsePrice($rawPrice) {
// Na wszelki wypadek rzutujemy na string i obcinamy spacje
$rawPrice = trim((string)$rawPrice);
if ($rawPrice === '') {
return 0.0;
}
// Zostawiamy tylko cyfry, przecinek, kropkę i minus
// Usuwamy walutę, spacje, itp.
$clean = preg_replace('/[^\d,.\-]/', '', $rawPrice);
$hasComma = strpos($clean, ',') !== false;
$hasDot = strpos($clean, '.') !== false;
// Przypadek typowo polski: "1234,56" lub "125,00"
if ($hasComma && !$hasDot) {
$clean = str_replace(',', '.', $clean);
}
// Przypadek mieszany: "1.234,56" -> usuń kropki (separatory tysięcy), przecinek zamień na kropkę
elseif ($hasComma && $hasDot) {
$clean = str_replace('.', '', $clean); // usuwamy separatory tysięcy
$clean = str_replace(',', '.', $clean); // przecinek na kropkę
}
// Jeśli jest tylko kropka ("1234.56") nic nie zmieniamy
return (float)$clean;
}
// Bazowa cena dla grupy najtańszy wariant (zwraca [brutto, netto])
function getBasePricesFromGroup($products) {
$minNet = null;
$minGross = null;
foreach ($products as $p) {
$gross = parsePrice((string)$p->price);
if ($gross <= 0) {
continue;
}
$net = Tools::ps_round($gross / 1.23, 6);
if ($minNet === null || $net < $minNet) {
$minNet = $net;
$minGross = $gross;
}
}
if ($minNet === null) {
return [0.0, 0.0];
}
return [$minGross, $minNet];
}
// Czyści log aktualizacji cen - zostawia tylko wpisy z ostatnich X dni
function cleanUpdateLog($logFile, $daysToKeep = 30) {
if (!file_exists($logFile)) {
return;
}
$cutoffDate = date('Y-m-d', strtotime('-'.(int)$daysToKeep.' days'));
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false || empty($lines)) {
return;
}
$newLines = [];
foreach ($lines as $line) {
$parts = explode(';', $line);
if (count($parts) < 1) {
// linia uszkodzona pomijamy
continue;
}
$logDate = trim($parts[0]);
// Jeśli data ma format YYYY-MM-DD i jest nowsza/równa cutoff zostawiamy
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $logDate) && $logDate >= $cutoffDate) {
$newLines[] = $line;
}
// Jeśli format daty jest dziwny na wszelki wypadek zachowajmy wpis
elseif (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $logDate)) {
$newLines[] = $line;
}
}
// Nadpisujemy plik przefiltrowaną zawartością
if (!empty($newLines)) {
file_put_contents($logFile, implode(PHP_EOL, $newLines) . PHP_EOL);
} else {
// Jeśli wszystko stare/uszkodzone czyścimy plik
file_put_contents($logFile, '');
}
}
// Zwraca id_tax_rules_group dla stawki VAT (np. 23) dla kraju domyślnego
function getTaxRulesGroupIdForRate($rate, $id_country = null) {
if ($id_country === null) {
$id_country = (int)Configuration::get('PS_COUNTRY_DEFAULT');
}
$sql = 'SELECT trg.`id_tax_rules_group`
FROM `'._DB_PREFIX_.'tax_rules_group` trg
INNER JOIN `'._DB_PREFIX_.'tax_rule` tr
ON (trg.`id_tax_rules_group` = tr.`id_tax_rules_group`)
INNER JOIN `'._DB_PREFIX_.'tax` t
ON (tr.`id_tax` = t.`id_tax`)
WHERE trg.`active` = 1
AND tr.`id_country` = '.(int)$id_country.'
AND t.`rate` = '.(float)$rate.'
ORDER BY trg.`id_tax_rules_group` ASC';
$id = Db::getInstance()->getValue($sql);
return $id ? (int)$id : 8;
}
// Zwraca główny produkt z grupy pierwszy, którego SKU istnieje w PrestaShop
function findMainProductDataFromGroup($products) {
foreach ($products as $p) {
$ref = (string)$p->sku;
if ($ref !== '' && findProductByReference($ref)) {
return $p; // to główny produkt
}
}
return null;
}
// === GRUPOWANIE PRODUKTÓW PO SYMBOLU ===
$productsBySymbol = [];
foreach ($xml->product as $productData) {
$symbol = (string)$productData->item_group_id;
if (!isset($productsBySymbol[$symbol])) {
$productsBySymbol[$symbol] = [];
}
$productsBySymbol[$symbol][] = $productData;
}
// ID grupy VAT 23%
$idTaxRulesGroup23 = 8;
// =======================================
// =========== TRYB AKTUALIZACJI =========
// =======================================
if ($modeUpdate) {
cleanUpdateLog($logFile, 30);
$today = date('Y-m-d');
$updatedToday = [];
// Wczytanie logu aktualizacji
if (file_exists($logFile)) {
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$parts = explode(';', $line);
if (count($parts) >= 3) {
$logDate = trim($parts[0]);
$logType = trim($parts[1]); // np. 'product'
$logRef = trim($parts[2]);
if ($logDate === $today) {
$updatedToday[$logType.'_'.$logRef] = true;
}
}
}
}
$updatedSomething = false;
foreach ($productsBySymbol as $symbol => $products) {
if (empty($products)) {
continue;
}
// Główny produkt referencja z pierwszego elementu grupy
// $mainProductData = $products[0];
// $reference = (string)$mainProductData->sku;
// $key = 'product_'.$reference;
$mainProductData = findMainProductDataFromGroup($products);
if (!$mainProductData) {
continue; // w grupie nie ma produktu, który istnieje w Presta
}
$reference = (string)$mainProductData->sku;
$key = 'product_'.$reference;
// Jeśli już zaktualizowany dzisiaj pomijamy
if (isset($updatedToday[$key])) {
continue;
}
$product = findProductByReference($reference);
if (!$product) {
// produkt nie istnieje w Presta - pomijamy w trybie update
continue;
}
// BAZA: najtańszy wariant w grupie (brutto i netto)
list($grossBase, $netPrice) = getBasePricesFromGroup($products);
if ($grossBase <= 0 || $netPrice <= 0) {
// brak sensownej ceny pomiń
continue;
}
// Flagi / liczniki
$updatedProductPrice = false;
$updatedCombinationCount = 0;
// Aktualizacja ceny produktu (NETTO) baza = najtańszy wariant
$product->price = $netPrice;
$updatedProductPrice = true;
// Produkt zawsze aktywny + delivery times
$product->active = 1;
$product->delivery_in_stock = createMultiLangField('2-7 dni roboczych');
$product->delivery_out_stock = createMultiLangField('4-10 tygodni');
// Upewnij się, że produkt ma ustawioną grupę VAT 23%
if (!empty($idTaxRulesGroup23) && (int)$product->id_tax_rules_group !== (int)$idTaxRulesGroup23) {
$product->id_tax_rules_group = (int)$idTaxRulesGroup23;
}
// Sprawdź kategorię domyślną jeśli "Strona główna", zamień na "Meble" (ID 107)
$id_lang = (int)$context->language->id;
$defaultCategory = new Category($product->id_category_default, $id_lang);
if (Validate::isLoadedObject($defaultCategory) && $defaultCategory->name == 'Strona główna') {
$newCategoryId = 107; // Meble
// Ustaw nową kategorię domyślną
$product->id_category_default = (int)$newCategoryId;
// Podmień kategorie produktu (zachowując ewentualne inne)
$categories = $product->getCategories();
$categories = array_diff($categories, [(int)$defaultCategory->id]);
$categories[] = (int)$newCategoryId;
$categories = array_unique(array_map('intval', $categories));
$product->updateCategories($categories);
}
// Zapis produktu
if ($product->update()) {
// --- STANY MAGAZYNOWE PRODUKTU (ID_PRODUCT_ATTRIBUTE = 0) ---
$mainStatus = (string)$mainProductData->Status_magazynowy;
$mainQty = ($mainStatus === 'instock') ? 100 : 0;
StockAvailable::setQuantity($product->id, 0, $mainQty);
// --- AKTUALIZACJA CEN I STANÓW KOMBINACJI NA PODSTAWIE XML ---
foreach ($products as $productDataVariant) {
$variantRef = (string)$productDataVariant->sku;
// Szukamy kombinacji po indeksie/SKU
$combination = findCombinationByReference($product->id, $variantRef);
if (!$combination) {
continue;
}
// Cena brutto wariantu z XML
$variantGross = parsePrice((string)$productDataVariant->price);
if ($variantGross <= 0) {
continue;
}
// Netto wariantu
$variantNet = Tools::ps_round($variantGross / 1.23, 6);
// Impact względem ceny bazowej produktu (najtańszy wariant)
$impact = $variantNet - $netPrice;
// zabezpieczenie na wypadek minimalnych różnic/zaokrągleń
if ($impact < 0) {
$impact = 0;
}
$impact = Tools::ps_round($impact, 6);
$combination->price = $impact;
// Stan magazynowy kombinacji
$variantStatus = (string)$productDataVariant->Status_magazynowy;
$variantQty = ($variantStatus === 'instock') ? 100 : 0;
StockAvailable::setQuantity($product->id, $combination->id, $variantQty);
if ($combination->update()) {
$updatedCombinationCount++;
}
}
// --- KONIEC AKTUALIZACJI KOMBINACJI ---
// Zapis do logu że ten produkt został dziś zaktualizowany
$logLine = $today.';product;'.$reference.';'.$product->id.PHP_EOL;
file_put_contents($logFile, $logLine, FILE_APPEND);
echo '<p><strong>Zaktualizowano produkt:</strong> '.htmlspecialchars((string)$mainProductData->title).' ('.$reference.')</p>';
echo '<p>Nowa bazowa cena brutto (najtańszy wariant) z XML: '.$grossBase.'</p>';
echo '<p>Nowa cena netto produktu (bazowa) w Presta: '.$netPrice.'</p>';
// INFO: czy zaktualizowano cenę produktu
if ($updatedProductPrice) {
echo '<p><strong>Cena produktu została zaktualizowana.</strong></p>';
} else {
echo '<p><strong>Cena produktu nie uległa zmianie.</strong></p>';
}
// INFO: ile kombinacji zaktualizowano
if ($updatedCombinationCount > 0) {
echo '<p><strong>Zaktualizowano ceny/ilości kombinacji:</strong> '.$updatedCombinationCount.' szt.</p>';
} else {
echo '<p><strong>Nie zaktualizowano żadnej kombinacji (brak dopasowanych SKU albo cen).</strong></p>';
}
$updatedSomething = true;
}
// Aktualizujemy tylko jeden produkt na jedno wywołanie
break;
}
if ($updatedSomething) {
// echo '<script>setTimeout(function(){ location.reload(); }, 50);</script>';
} else {
echo '<p>Brak produktów do aktualizacji na dzisiaj (wszystkie z XML zostały już zaktualizowane).</p>';
}
exit;
}
// =======================================
// =========== TRYB DODAWANIA ============
// =======================================
if ($modeAdd) {
$productAdded = false;
$combinationAdded = false;
// Tworzenie lub aktualizacja produktów z kombinacjami (dodawanie)
foreach ($productsBySymbol as $symbol => $products) {
if (empty($products)) {
continue;
}
// ===== Filter item_group_id =====
// $mainProductDataTemp = $products[0];
// if ((string)$mainProductDataTemp->item_group_id !== '68590') {
// continue;
// }
// Główny produkt wybieramy deterministycznie
$mainProductData = findMainProductDataFromGroup($products);
if (!$mainProductData) {
// fallback: jeśli żaden nie istnieje, wybierz alfabetycznie najniższy SKU
usort($products, function($a, $b) {
return strcmp((string)$a->sku, (string)$b->sku);
});
$mainProductData = $products[0];
}
$mainProduct = findProductByReference((string)$mainProductData->sku);
// BAZA: najtańszy wariant w grupie (brutto i netto)
list($grossBase, $netPrice) = getBasePricesFromGroup($products);
if (!$mainProduct) {
// Create a new product if it doesn't exist
$mainProduct = new Product();
$mainProduct->name = createMultiLangField((string)$mainProductData->title);
$description = (string)$mainProductData->description;
$description = str_replace("\n", "<br>", $description);
$mainProduct->description = createMultiLangField($description);
// Cena BRUTTO z XML -> NETTO (23%) bazą jest najtańszy wariant
$mainProduct->price = $netPrice > 0 ? $netPrice : 0;
// VAT 23% jeśli dostępny
$mainProduct->id_tax_rules_group = 8;
// Produkt aktywny + delivery times
$mainProduct->active = 1;
$mainProduct->delivery_in_stock = createMultiLangField('2-7 dni roboczych');
$mainProduct->delivery_out_stock = createMultiLangField('4-10 tygodni');
$mainProduct->reference = (string)$mainProductData->sku;
$mainProduct->id_category_default = 107; // np. Meble
$mainProduct->link_rewrite = createLinkRewrite((string)$mainProductData->title);
// Najpierw dodaj produkt
$mainProduct->add();
$mainProduct = new Product($mainProduct->id);
$mainProduct->id_tax_rules_group = 8;
$mainProduct->update();
// ===== Poprawne przypisanie kategorii =====
$id_lang = (int)$context->language->id;
$defaultCategory = new Category($mainProduct->id_category_default, $id_lang);
if (Validate::isLoadedObject($defaultCategory) && $defaultCategory->name == 'Strona główna') {
$newCategoryId = 107; // Meble
$mainProduct->id_category_default = (int)$newCategoryId;
// Podmieniamy kategorie produktu (zachowując inne)
$categories = $mainProduct->getCategories();
$categories = array_diff($categories, [(int)$defaultCategory->id]);
$categories[] = (int)$newCategoryId;
$categories = array_unique(array_map('intval', $categories));
$mainProduct->updateCategories($categories);
}
// ===== Koniec ustawienia kategorii =====
// Add images to the product
$imageUrls = getAllImageUrlsFromXmlProduct($mainProductData, 10);
$first = true;
foreach ($imageUrls as $url) {
addProductImage($mainProduct->id, $url, $first);
$first = false;
}
$productAdded = true;
}
// Ensure the product is saved before adding combinations
if (!$mainProduct->id) {
echo "Failed to create or update main product: " . (string)$mainProductData->title . "\n";
continue;
}
// Ensure the combination set is unique for the product
$addedCombinations = [];
// Add or update combinations for each product in the group
foreach ($products as $productData) {
$attributes = [
'Kolor' => (string)$productData->Kolor,
'Długość' => (string)$productData->Dlugosc,
'Szerokość' => (string)$productData->Szerokosc,
'Głębokość' => (string)$productData->Glebokosc,
'Wysokość' => (string)$productData->Wysokosc,
];
$attributeIds = [];
foreach ($attributes as $name => $value) {
if (!empty($value)) {
$attributeGroupId = createAttribute($name, [$value]);
$attribute = findAttributeByName($attributeGroupId, $value);
if ($attribute) {
$attributeIds[] = (int)$attribute->id;
}
}
}
// Create a unique key for the attribute set
sort($attributeIds);
$key = implode('-', $attributeIds);
// Add or update combination if it is unique
if (!empty($attributeIds) && !isset($addedCombinations[$key])) {
$combination = findCombinationByAttributes($mainProduct->id, $attributeIds);
// Oblicz cenę wariantu
$variantGross = parsePrice((string)$productData->price);
$variantNet = $variantGross > 0 ? Tools::ps_round($variantGross / 1.23, 6) : 0;
// Impact względem ceny bazowej produktu
$impact = $variantNet - $netPrice;
if ($impact < 0) {
$impact = 0; // zabezpieczenie
}
$impact = Tools::ps_round($impact, 6);
if (!$combination) {
// Create new combination
$combination = new Combination();
$combination->id_product = (int)$mainProduct->id;
$combination->quantity = 100; // startowo, i tak zaraz nadpiszemy StockAvailable
$combination->reference = (string)$productData->sku;
$combination->price = $impact; // wpływ na cenę (tax excluded)
$combination->ecotax = 0;
$combination->wholesale_price = 0;
$combination->default_on = 0;
$combination->add();
$combination->setAttributes($attributeIds);
// Dodatkowy update aby upewnić się że cena jest prawidłowo zapisana
$combination->price = $impact;
$combination->update();
$combinationAdded = true;
} else {
// Update existing combination
$combination->quantity = 100; // startowo
$combination->price = $impact; // aktualizacja ceny
$combination->update();
}
// Mark this combination as added
$addedCombinations[$key] = true;
}
if ($combinationAdded) {
break; // Break if a new combination was added
}
}
// --- STANY MAGAZYNOWE PRODUKTU (ID_PRODUCT_ATTRIBUTE = 0) ---
$mainStatus = (string)$mainProductData->Status_magazynowy;
$mainQty = ($mainStatus === 'instock') ? 100 : 0;
StockAvailable::setQuantity($mainProduct->id, 0, $mainQty);
// --- STANY MAGAZYNOWE KOMBINACJI ---
foreach ($products as $productDataVariant) {
$variantRef = (string)$productDataVariant->sku;
$variantStatus = (string)$productDataVariant->Status_magazynowy;
$variantQty = ($variantStatus === 'instock') ? 100 : 0;
$combination = findCombinationByReference($mainProduct->id, $variantRef);
if ($combination) {
StockAvailable::setQuantity($mainProduct->id, $combination->id, $variantQty);
}
}
// Ensure the product has combinations enabled
$mainProduct->checkDefaultAttributes();
Product::updateDefaultAttribute($mainProduct->id);
// Upewnij się że produkt główny ma poprawne ustawienie VAT po dodaniu kombinacji
if ($productAdded || $combinationAdded) {
$mainProduct = new Product($mainProduct->id); // Przeładuj produkt
if (!empty($idTaxRulesGroup23) && (int)$mainProduct->id_tax_rules_group !== (int)$idTaxRulesGroup23) {
$mainProduct->id_tax_rules_group = (int)$idTaxRulesGroup23;
$mainProduct->update();
}
}
if ($productAdded || $combinationAdded) {
if ($productAdded) {
echo "<p>Dodałem produkt: " . htmlspecialchars((string)$mainProductData->title) . "</p>";
}
if ($combinationAdded) {
echo "<p>Dodałem kombinację: " . htmlspecialchars((string)$mainProductData->title) . "</p>";
}
break; // Break if a new product or combination was added
}
}
echo '<p>Dodawanie zakończone.</p>';
exit;
}
// =======================================
// ========= TRYB SYNCHRONIZACJI =========
// =======================================
if ($modeSynch) {
echo '<h2>Rozpoczynam synchronizację produktów...</h2>';
$db = Db::getInstance();
// 1. Wyczyszczenie tymczasowej tabeli
$db->execute('TRUNCATE TABLE `'._DB_PREFIX_.'drewmax_products_temp`');
// 1.1 Wczytywanie SKU z XML i dodawanie do tymczasowej tabeli
$skusInXml = [];
foreach ($xml->product as $productData) {
$sku = trim((string)$productData->sku);
if ($sku === '') continue;
$skusInXml[] = $sku;
$db->execute('
INSERT INTO `'._DB_PREFIX_.'drewmax_products_temp` (`sku`, `date_checked`)
VALUES (\''.pSQL($sku).'\', NOW())
');
}
// 2. Praca z historią
if (!empty($skusInXml)) {
// Utworzenie listy SKU dla SQL
$skusList = "'" . implode("','", array_map('pSQL', $skusInXml)) . "'";
// 2.1 Dodawanie nowych SKU do historii lub przywracanie aktywności
$sqlInsert = '
INSERT INTO `'._DB_PREFIX_.'drewmax_products_history` (`sku`, `active`)
SELECT t.sku, 1
FROM `'._DB_PREFIX_.'drewmax_products_temp` t
LEFT JOIN `'._DB_PREFIX_.'drewmax_products_history` h ON h.sku = t.sku
WHERE h.sku IS NULL
';
$db->execute($sqlInsert);
$sqlUpdateActive = '
UPDATE `'._DB_PREFIX_.'drewmax_products_history` h
JOIN `'._DB_PREFIX_.'drewmax_products_temp` t ON h.sku = t.sku
SET h.active = 1
WHERE h.active = 0
';
$db->execute($sqlUpdateActive);
}
// 2.2 Wykluczanie produktów, których nie ma w temp
$sqlDeactivate = '
UPDATE `'._DB_PREFIX_.'drewmax_products_history` h
LEFT JOIN `'._DB_PREFIX_.'drewmax_products_temp` t ON h.sku = t.sku
SET h.active = 0
WHERE t.sku IS NULL AND h.active = 1
';
$db->execute($sqlDeactivate);
// Wyłącz produkty w PrestaShop dla SKU, które mają active=0
$skusToDisable = $db->executeS('
SELECT sku FROM `'._DB_PREFIX_.'drewmax_products_history`
WHERE active = 0
');
// 3. Synchronizacja statusu produktów i kombinacji w PrestaShop
// Pobieramy tylko SKU z historii (źródło prawdy)
$allHistory = $db->executeS('
SELECT sku, active
FROM `'._DB_PREFIX_.'drewmax_products_history`
');
foreach ($allHistory as $row) {
$sku = trim($row['sku']);
$activeStatus = (int)$row['active'];
if ($sku === '') {
continue;
}
/**
* KROK 3.1
* Sprawdzenie czy SKU jest GŁÓWNYM PRODUKTEM
* (tylko ps_product.reference)
*/
$idProduct = (int)$db->getValue('
SELECT id_product
FROM `'._DB_PREFIX_.'product`
WHERE reference = \''.pSQL($sku).'\'
LIMIT 1
');
if ($idProduct > 0) {
// === GŁÓWNY PRODUKT ===
$product = new Product($idProduct);
if (!Validate::isLoadedObject($product)) {
continue;
}
if ($activeStatus === 0 && (int)$product->active === 1) {
// Wyłącz produkt
$product->active = 0;
$product->update();
echo "Wyłączono PRODUKT: {$sku}<br>";
}
if ($activeStatus === 1 && (int)$product->active === 0) {
// Włącz produkt
$product->active = 1;
$product->update();
echo "Włączono PRODUKT: {$sku}<br>";
}
// Główny produkt obsłużony → NIE sprawdzamy kombinacji
continue;
}
/**
* KROK 3.2
* Jeśli NIE jest produktem → sprawdzamy czy to KOMBINACJA
*/
$combinationRow = $db->getRow('
SELECT id_product_attribute, id_product
FROM `'._DB_PREFIX_.'product_attribute`
WHERE reference = \''.pSQL($sku).'\'
LIMIT 1
');
if ($combinationRow) {
// === KOMBINACJA ===
if ($activeStatus === 0) {
$idProductAttribute = (int)$combinationRow['id_product_attribute'];
$combination = new Combination($idProductAttribute);
if (Validate::isLoadedObject($combination)) {
$combination->delete();
echo "Usunięto KOMBINACJĘ: {$sku} (ID: {$idProductAttribute})<br>";
}
}
// Jeśli active = 1 → NIC NIE ROBIMY (kombinacji się nie aktywuje)
continue;
}
/**
* KROK 3.3
* SKU nie istnieje w PrestaShop → ignorujemy
*/
}
echo '<h3>Synchronizacja zakończona.</h3>';
}
// reload page after 250ms if product or combination was added (dla trybu add)
if ($productAdded || $combinationAdded) {
// echo "<script>setTimeout(function(){location.reload();}, 250);</script>";
}
?>