controller) {
$context->controller = new FrontController();
$context->controller->init();
}
}
// Гарантиране на shop контекст
if (Shop::isFeatureActive()) {
if (!Shop::getContext()) {
Shop::setContext(Shop::CONTEXT_SHOP, (int)Configuration::get('PS_SHOP_DEFAULT'));
}
}
} catch (Exception $e) {
http_response_code(500);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(array('error' => 'PrestaShop initialization failed: ' . $e->getMessage()));
exit;
}
// ---------- ГАРАНТИРАН КОНТЕКСТ ----------
$context = Context::getContext();
// Shop
if (!$context->shop || !(int)$context->shop->id) {
$idShop = (int)Configuration::get('PS_SHOP_DEFAULT');
if ($idShop) {
$context->shop = new Shop($idShop);
Shop::setContext(Shop::CONTEXT_SHOP, $idShop);
}
}
// Language
if (!$context->language || !(int)$context->language->id) {
$idLang = (int)Configuration::get('PS_LANG_DEFAULT');
if ($idLang) $context->language = new Language($idLang);
}
// Currency
if (!$context->currency || !(int)$context->currency->id) {
$idCur = (int)Configuration::get('PS_CURRENCY_DEFAULT');
if ($idCur) $context->currency = new Currency($idCur);
}
// Country
if (!$context->country || !(int)$context->country->id) {
$idCountry = (int)Configuration::get('PS_COUNTRY_DEFAULT');
if ($idCountry) $context->country = new Country($idCountry);
}
$link = $context->link;
// ---------- ПОМОЩНИ ФУНКЦИИ ----------
function ps_get_active_languages() {
static $langs = null;
if ($langs === null) {
$langs = Language::getLanguages(true, Context::getContext()->shop->id);
}
return $langs;
}
function ps_currency() {
static $cur = null;
if ($cur === null) {
$defaultId = (int)Configuration::get('PS_CURRENCY_DEFAULT');
$cur = new Currency($defaultId);
}
return $cur;
}
// Активни продукти (ID desc)
function ps_fetch_active_products($offset=0, $limit=100000) {
$sql = (new DbQuery())
->select('p.id_product, p.reference')
->from('product', 'p')
->innerJoin('product_shop', 'ps', 'p.id_product = ps.id_product AND ps.id_shop = '.(int)Context::getContext()->shop->id)
->where('ps.active = 1')
->orderBy('p.id_product DESC')
->limit((int)$limit, (int)$offset);
$rows = Db::getInstance()->executeS($sql);
return $rows ? $rows : array();
}
// Имена и link_rewrite за всички активни езици
function ps_fetch_names_for_product($id_product) {
$langs = ps_get_active_languages();
$ids = array();
foreach ($langs as $l) $ids[] = (int)$l['id_lang'];
if (!$ids) return array();
$sql = (new DbQuery())
->select('pl.id_lang, pl.name, pl.link_rewrite')
->from('product_lang', 'pl')
->where('pl.id_product='.(int)$id_product)
->where('pl.id_shop='.(int)Context::getContext()->shop->id)
->where('pl.id_lang IN ('.implode(',', $ids).')');
$rows = Db::getInstance()->executeS($sql);
$out = array();
if ($rows) {
foreach ($rows as $r) {
$out[(int)$r['id_lang']] = array(
'name' => $r['name'],
'link_rewrite' => $r['link_rewrite'],
);
}
}
return $out;
}
// id на кавър изображението
function ps_cover_image_id($id_product) {
$cover = Image::getCover($id_product);
if ($cover && !empty($cover['id_image'])) return (int)$cover['id_image'];
return null;
}
// URL за миниатюра + URL към продукта
function ps_image_and_product_links($id_product, $namesByLang, $link) {
$langs = ps_get_active_languages();
$firstLangId = (int)$langs[0]['id_lang'];
$linkRewrite = isset($namesByLang[$firstLangId]['link_rewrite']) ? $namesByLang[$firstLangId]['link_rewrite'] : 'product';
$coverId = ps_cover_image_id($id_product);
// Определяне на типа изображение
$thumbType = 'home_default';
if (class_exists('ImageType')) {
if (method_exists('ImageType','getFormattedName')) {
$thumbType = ImageType::getFormattedName('home');
} elseif (method_exists('ImageType','getFormatedName')) {
$thumbType = ImageType::getFormatedName('home');
}
}
$imgUrl = $coverId ? $link->getImageLink($linkRewrite, $coverId, $thumbType) : null;
$productUrl = $link->getProductLink($id_product, $linkRewrite, null, null, $firstLangId);
return array($imgUrl, $productUrl);
}
// Наличности по комбинации
function ps_sizes_and_qty($id_product) {
$langs = ps_get_active_languages();
$id_lang = (int)$langs[0]['id_lang'];
$prod = new Product($id_product, false, $id_lang, Context::getContext()->shop->id);
$combs = $prod->getAttributeCombinations($id_lang);
if (!$combs) $combs = array();
$byAttr = array();
foreach ($combs as $c) {
$ipa = (int)$c['id_product_attribute'];
if (!isset($byAttr[$ipa])) $byAttr[$ipa] = array('attrs'=>array(), 'ipa'=>$ipa);
$gname = isset($c['group_name']) ? mb_strtolower($c['group_name']) : '';
$aname = isset($c['attribute_name']) ? $c['attribute_name'] : '';
$byAttr[$ipa]['attrs'][] = array('group'=>$gname, 'name'=>$aname);
}
$preferSize = array();
foreach ($byAttr as $ipa => $data) {
foreach ($data['attrs'] as $a) {
if (preg_match('/(size|размер)/iu', $a['group'])) { $preferSize[$ipa] = true; break; }
}
}
$rows = array();
foreach ($byAttr as $ipa => $data) {
$qty = (int)StockAvailable::getQuantityAvailableByProduct($id_product, $ipa, (int)Context::getContext()->shop->id);
$sizeLabel = null;
if (!empty($preferSize[$ipa])) {
foreach ($data['attrs'] as $a) {
if (preg_match('/(size|размер)/iu', $a['group'])) { $sizeLabel = $a['name']; break; }
}
}
if ($sizeLabel === null) {
$parts = array(); foreach ($data['attrs'] as $a) { $parts[] = $a['name']; }
$sizeLabel = implode(' / ', $parts);
}
$rows[] = array('label'=>$sizeLabel, 'qty'=>$qty);
}
$withStock = array(); foreach ($rows as $r) { if ($r['qty'] > 0) $withStock[] = $r; }
return $withStock ? $withStock : $rows;
}
// Общо наличност
function ps_total_qty($id_product) {
return (int)StockAvailable::getQuantityAvailableByProduct($id_product, 0, (int)Context::getContext()->shop->id);
}
// Цени
function ps_prices($id_product) {
$ctx = Context::getContext();
$idShop = (int)$ctx->shop->id;
$idCur = (int)$ctx->currency->id;
// Базова цена от product_shop
$q = new DbQuery();
$q->select('ps.price')
->from('product_shop', 'ps')
->where('ps.id_product='.(int)$id_product)
->where('ps.id_shop='.(int)$idShop);
$row = Db::getInstance()->getRow($q);
$base = $row ? (float)$row['price'] : 0.0;
// Търсене на специфични цени
$now = date('Y-m-d H:i:s');
$spQ = new DbQuery();
$spQ->select('reduction, reduction_type')
->from('specific_price', 'sp')
->where('sp.id_product='.(int)$id_product)
->where('sp.id_shop IN (0,'.(int)$idShop.')')
->where('sp.id_currency IN (0,'.(int)$idCur.')')
->where('sp.id_country IN (0,'.(int)$ctx->country->id.')')
->where('sp.id_group = 0')
->where('sp.id_customer = 0')
->where('(sp.from = "0000-00-00 00:00:00" OR sp.from <= "'.$now.'")')
->where('(sp.to = "0000-00-00 00:00:00" OR sp.to >= "'.$now.'")')
->orderBy('sp.id_specific_price DESC');
$sp = Db::getInstance()->getRow($spQ);
$current = $base;
if ($sp && (float)$sp['reduction'] > 0) {
if ($sp['reduction_type'] === 'percentage') {
$current = $base * (1.0 - (float)$sp['reduction']);
} else { // amount
$current = $base - (float)$sp['reduction'];
}
if ($current < 0) $current = 0.0;
}
// ДОБАВЕТЕ ТУК УМНОЖЕНИЕТО С 1.2 ЗА ДДС
$base_with_vat = $base * 1.2;
$current_with_vat = $current * 1.2;
return array((float)$base, (float)$current);
}
// Форматиране на цена
function ps_fmt_price($price) {
// Добавяне на ДДС
$price_with_vat = $price * 1.2;
return Tools::displayPrice($price, ps_currency());
}
// Събиране на данните
function ps_collect_data() {
$products = ps_fetch_active_products(0, 100000);
$langs = ps_get_active_languages();
$out = array();
foreach ($products as $p) {
$id = (int)$p['id_product'];
$ref = $p['reference'];
$names = ps_fetch_names_for_product($id);
list($imgUrl, $pUrl) = ps_image_and_product_links($id, $names, Context::getContext()->link);
// Имена за всички активни езици
$nameParts = array();
foreach ($langs as $l) {
$id_lang = (int)$l['id_lang'];
$iso = $l['iso_code'];
$nm = isset($names[$id_lang]['name']) ? $names[$id_lang]['name'] : '';
if ($nm !== '') { $nameParts[] = '['.$iso.'] '.$nm.'
'; }
}
$namesAll = implode("\n", $nameParts);
// Размери + количества
$sizes = ps_sizes_and_qty($id);
$sizeParts = array();
foreach ($sizes as $s) {
$label = isset($s['label']) ? $s['label'] : '';
$qty = isset($s['qty']) ? (int)$s['qty'] : 0;
$sizeParts[] = $label . ': ' . $qty;
}
$sizesStr = implode("\n", $sizeParts);
$totalQty = ps_total_qty($id);
list($base, $curr) = ps_prices($id);
// ДОБАВЕТЕ ДДС ТУК ДИРЕКТНО
$base_with_vat = $base * 1.2;
$curr_with_vat = $curr * 1.2;
$out[] = array(
'id' => $id,
'reference' => $ref,
'image' => $imgUrl,
'plink' => $pUrl,
'names' => $namesAll,
'sizes' => $sizesStr,
'total' => $totalQty,
'base' => ps_fmt_price($base * 1.2), //Това умножение по 1,2 се прави за да показва цената с ДДС
'current' => ps_fmt_price($curr * 1.2), //Това умножение по 1,2 се прави за да показва цената с ДДС
'has_disc' => ($curr < $base) ? 1 : 0,
//'disc_percent' => ($base > 0 && $curr < $base) ? (int)round((($base - $curr) / $base) * 100) : 0,
'disc_percent' => ($base > 0 && $curr < $base)
? (int) round((($base - $curr) / $base) * 100)
: 0,
);
}
return $out;
}
// ---------- РЕЖИМИ (AJAX/EXPORT/VIEW) ----------
if (isset($_GET['action']) && $_GET['action'] === 'data') {
header('Content-Type: application/json; charset=utf-8');
try {
$data = ps_collect_data();
echo json_encode(array('data'=>$data), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} catch (Exception $e) {
echo json_encode(array('error'=>'Грешка при събиране на данни: ' . $e->getMessage()));
}
exit;
}
// Експорт към Excel (CSV UTF-8 BOM)
if (isset($_GET['export']) && $_GET['export'] === '1') {
try {
$rows = ps_collect_data();
$filename = 'products_export_' . date('Ymd_His') . '.csv';
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
echo "\xEF\xBB\xBF";
$out = fopen('php://output', 'w');
fputcsv($out, array(
'ID','Reference','Product URL','Image URL','Names (all languages)',
'Sizes (qty)','Total Qty','Base Price','Current Price','Discount %',
), ';');
foreach ($rows as $r) {
fputcsv($out, array(
$r['id'],
$r['reference'],
$r['plink'],
$r['image'],
preg_replace("/\r?\n/", ' | ', $r['names']),
preg_replace("/\r?\n/", ' | ', $r['sizes']),
$r['total'],
$r['base'],
$r['current'],
($r['disc_percent'] > 0 ? ('-' . $r['disc_percent'] . '%') : ''),
), ';');
}
fclose($out);
} catch (Exception $e) {
header('Content-Type: text/plain; charset=utf-8');
echo 'Грешка при експорт: ' . $e->getMessage();
}
exit;
}
?>
Активни продукти – репорт
Активни продукти / Неактивни с наличност за продажба!
Зареждане на данни…
Може да отнеме малко време при много продукти.
ID
Ref
Снимка
Име (всички езици)
Размери (наличности)
Общо наличност
Цена (с ДДС)