Files
masimmo.pl/import-drewmax.php

529 lines
19 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');
if (!$modeAdd && !$modeUpdate) {
die('Brak akcji. Dodaj do adresu ?add=true lub ?update=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 to download image from URL and associate it with a product
function addProductImage($productId, $imageUrl)
{
$image = new Image();
$image->id_product = (int)$productId;
$image->position = Image::getHighestPosition($productId) + 1;
$image->cover = true; // Set the first image as cover
$image->add();
$imagePath = $image->getPathForCreation();
$url = str_replace(' ', '%20', $imageUrl);
if (!copy($url, $imagePath . '.jpg')) {
$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;
}
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;
}
// 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 : 0;
}
// === 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;
}
$idTaxRulesGroup23 = getTaxRulesGroupIdForRate(23);
// =======================================
// =========== 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;
// 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;
}
// Nowa cena z XML (BRUTTO 23%)
$grossPrice = parsePrice((string)$mainProductData->price);
if ($grossPrice <= 0) {
// brak sensownej ceny pomiń
continue;
}
// Przeliczenie brutto -> netto (23%)
$netPrice = Tools::ps_round($grossPrice / 1.23, 6);
// Aktualizacja ceny produktu (NETTO)
$product->price = $netPrice;
// 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();
// Usuń starą kategorię domyślną, jeśli istnieje w tablicy
$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()) {
// 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>Zaktualizowano produkt: '.htmlspecialchars((string)$mainProductData->title).' ('.$reference.')</p>';
echo '<p>Nowa cena brutto z XML: '.$grossPrice.'</p>';
echo '<p>Nowa cena netto zapisana w Presta: '.$netPrice.'</p>';
$updatedSomething = true;
}
// Aktualizujemy tylko jeden produkt na jedno wywołanie
break;
}
if ($updatedSomething) {
// Odśwież stronę, żeby przy kolejnym wywołaniu zaktualizować następny produkt
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 ============
// =======================================
$productAdded = false;
$combinationAdded = false;
// Tworzenie lub aktualizacja produktów z kombinacjami (dodawanie)
foreach ($productsBySymbol as $symbol => $products) {
if (empty($products)) {
continue;
}
// Get the main product data from the first product in the group
$mainProductData = $products[0];
$mainProduct = findProductByReference((string)$mainProductData->sku);
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%)
$grossPrice = parsePrice((string)$mainProductData->price);
$netPrice = Tools::ps_round($grossPrice / 1.23, 6);
$mainProduct->price = $netPrice;
// VAT 23% jeśli dostępny
if (!empty($idTaxRulesGroup23)) {
$mainProduct->id_tax_rules_group = (int)$idTaxRulesGroup23;
}
$mainProduct->reference = (string)$mainProductData->sku;
$mainProduct->id_category_default = 2; // np. Strona główna
$mainProduct->link_rewrite = createLinkRewrite((string)$mainProductData->title);
$mainProduct->add();
// Add images to the product
if (!empty($mainProductData->image)) {
addProductImage($mainProduct->id, (string)$mainProductData->image);
}
for ($i = 1; $i <= 10; $i++) {
$imageUrl = (string)$mainProductData->{'images_' . $i};
if (!empty($imageUrl)) {
addProductImage($mainProduct->id, $imageUrl);
}
}
$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,
'Dlugosc' => (string)$productData->Dlugosc,
'Szerokosc' => (string)$productData->Szerokosc,
'Glebokosc' => (string)$productData->Glebokosc,
'Wysokosc' => (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);
if (!$combination) {
// Create new combination
$combination = new Combination();
$combination->id_product = (int)$mainProduct->id;
$combination->quantity = 100; // Default quantity, you can adjust this
$combination->reference = (string)$productData->sku;
$combination->add();
$combination->setAttributes($attributeIds);
$combination->save();
$combinationAdded = true;
} else {
// Update existing combination quantity if necessary
$combination->quantity = 100; // Update quantity, you can adjust this
$combination->update();
}
// Mark this combination as added
$addedCombinations[$key] = true;
}
if ($combinationAdded) {
break; // Break if a new combination was added
}
}
// Ensure the product has combinations enabled
$mainProduct->checkDefaultAttributes();
Product::updateDefaultAttribute($mainProduct->id);
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
}
}
// reload page after 250ms if product or combination was added (dla trybu add)
if ($productAdded || $combinationAdded) {
// echo "<script>setTimeout(function(){location.reload();}, 250);</script>";
}
?>