commit fa5baeca0c11f229f09e8caefaff2b2e5d57a145 Author: MrAkells Date: Thu Sep 25 14:46:36 2025 +0300 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5c1a35 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# YML → CSV (open endpoint for WP All Import) + +Генерирует CSV из Prom.ua YML-фида по открытому эндпоинту. Строит `category_path` для иерархии категорий и конвертирует цену из PLN в UAH (НБУ или вручную). + +## Установка + +1. Создай папку `wp-content/plugins/yml2csv/`. +2. Сохрани файл плагина как `wp-content/plugins/yml2csv/yml2csv.php`. +3. Активируй плагин в админке WordPress. + +## Эндпоинт + +`GET /?yml2csv=1&src=...&target_cur=UAH[&rate=...][&margin=...][&precision=...]` + +**Параметры:** + +* `src` — URL YML-фида (обязательный). +* `target_cur` — целевая валюта. Автоконверсия реализована для `UAH` при исходном `PLN`. +* `rate` — фиксированный курс PLN→UAH. Если задан, НБУ не вызывается. +* `margin` — множитель наценки (например, `1.07` = +7%). По умолчанию `1.0`. +* `precision` — знаков после запятой для финальной цены. По умолчанию `2`. + +## Примеры + +```text +https://example.com/?yml2csv=1&src=http://127.0.0.1/feeds/prom_feed.yml&target_cur=UAH +https://example.com/?yml2csv=1&src=http://127.0.0.1/feeds/prom_feed.yml&target_cur=UAH&rate=9.25 +https://example.com/?yml2csv=1&src=http://127.0.0.1/feeds/prom_feed.yml&target_cur=UAH&margin=1.07&precision=0 +``` + +## Формат CSV + +Колонки: + +``` +sku, title, description, +price, currency, // конечные значения для WooCommerce +price_src, currency_src, // исходные значения из фида +stock, +category_path, category_name, category_id, +image_urls // через запятую +``` + +## Маппинг в WP All Import + +* Title → `title` +* Content → `description` +* Images → `image_urls` (comma-separated) +* Regular Price → `price` +* SKU → `sku` +* Categories → включить «hierarchical (parent/child)» и указать поле `category_path` +* (опц.) сохранить `price_src` / `currency_src` как метаполя + +## Лицензия + +MIT. + diff --git a/yml2csv.php b/yml2csv.php new file mode 100644 index 0000000..a1a31ea --- /dev/null +++ b/yml2csv.php @@ -0,0 +1,220 @@ + 25, 'redirection' => 5]); + if (is_wp_error($res)) return null; + $code = wp_remote_retrieve_response_code($res); + if ($code < 200 || $code >= 300) return null; + $body = wp_remote_retrieve_body($res); + if (!$body) return null; + libxml_use_internal_errors(true); + $xml = simplexml_load_string($body); + return $xml ?: null; +} + +/** Вытаскиваем категории (id, parentId, name) */ +function y2c_extract_categories(SimpleXMLElement $xml): array { + $out = []; + $nodes = $xml->shop->categories->category ?? $xml->categories->category ?? null; + if (!$nodes) return $out; + + foreach ($nodes as $c) { + $id = (string)$c['id']; + if ($id === '') continue; + $out[$id] = [ + 'id' => $id, + 'parent' => isset($c['parentId']) ? (string)$c['parentId'] : '', + 'name' => trim((string)$c), + ]; + } + return $out; +} + +/** Строим текстовые пути категорий "Parent > Child > Leaf" */ +function y2c_build_paths(array $cats): array { + $paths = []; + foreach ($cats as $id => $_) { + if (isset($paths[$id])) continue; + $chain = []; + $cur = $id; + $seen = []; + while ($cur && !isset($paths[$cur])) { + if (isset($seen[$cur])) { $chain = []; break; } // защита от цикла + $seen[$cur] = true; + $chain[] = $cur; + $cur = $cats[$cur]['parent'] ?? ''; + } + $prefix = ($cur && isset($paths[$cur])) ? $paths[$cur] : []; + for ($i = count($chain)-1; $i >= 0; $i--) { + $cid = $chain[$i]; + $prefix[] = $cats[$cid]['name'] ?? ''; + $paths[$cid] = $prefix; + } + } + $out = []; + foreach ($paths as $id => $arr) $out[$id] = implode(' > ', array_filter($arr)); + return $out; +} + +/** Курс PLN→UAH из НБУ */ +function y2c_rate_pln_to_uah(): float { + // НБУ JSON: [{"r030":985,"txt":"Злотий польський","rate":...,"cc":"PLN","exchangedate":"..."}] + $res = wp_remote_get('https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?valcode=PLN&json', ['timeout'=>10]); + if (is_wp_error($res)) return 0.0; + if (wp_remote_retrieve_response_code($res) !== 200) return 0.0; + $data = json_decode(wp_remote_retrieve_body($res), true); + if (!is_array($data) || empty($data[0]['rate'])) return 0.0; + return (float)$data[0]['rate']; +} + +/** + * Формирование строк офферов + конвертация цены. + * - Если задан rate>0 — используем его (любая исходная валюта будет трактоваться как PLN для умножения). + * - Если rate не задан и target_cur=UAH, то: + * - если исходная валюта = PLN → умножаем на курс НБУ, + * - иначе оставляем как есть (fail-safe). + * - margin применяется поверх конверсии, после чего округляем. + */ +function y2c_extract_offers_with_fx( + SimpleXMLElement $xml, + array $cats, + array $paths, + string $targetCur, + float $rateParam, + float $nbuRate, + float $margin, + int $precision +): array { + $offers = $xml->shop->offers->offer ?? $xml->offers->offer ?? null; + $rows = []; + if (!$offers) return $rows; + + foreach ($offers as $o) { + $sku = (string)($o->vendorCode ?? $o['id'] ?? ''); + $title = trim((string)($o->name ?? $o->model ?? $o->title ?? '')); + $desc = trim((string)($o->description ?? '')); + $priceSrc = (float)($o->price ?? 0); + $curSrc = strtoupper((string)($o->currencyId ?? 'PLN')); // по умолчанию считаем PLN, если нет поля + + // Базово — без конверсии + $priceOut = $priceSrc; + $curOut = $curSrc; + + // Конвертация + if ($targetCur !== '') { + if ($rateParam > 0) { + // Принудительный курс + $priceOut = $priceSrc * $rateParam; + $curOut = $targetCur; + } elseif ($targetCur === 'UAH' && $curSrc === 'PLN' && $nbuRate > 0) { + $priceOut = $priceSrc * $nbuRate; + $curOut = 'UAH'; + } + } + + // Наценка (если есть) + if ($margin > 0 && $margin !== 1.0) { + $priceOut = $priceOut * $margin; + } + + // Округление + $priceOut = ($precision >= 0) ? round($priceOut, $precision) : $priceOut; + + $stock = (string)($o->quantity ?? $o->stock_quantity ?? ''); + $catId = (string)($o->categoryId ?? ''); + $catName = $cats[$catId]['name'] ?? ''; + $catPath = $paths[$catId] ?? $catName; + + $imgs = []; + if (isset($o->picture)) { + foreach ($o->picture as $p) { $u = trim((string)$p); if ($u) $imgs[] = $u; } + } elseif (isset($o->images)) { + foreach ($o->images->image as $p) { $u = trim((string)$p); if ($u) $imgs[] = $u; } + } + + $rows[] = [ + $sku, + $title, + $desc, + $priceOut, + $curOut, + $priceSrc, + $curSrc, + $stock, + $catPath, + $catName, + $catId, + implode(',', $imgs), + ]; + } + return $rows; +} +