CaTzArOv

All active products in prestashop

Nov 17th, 2025
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 22.33 KB | Source Code | 0 0
  1. <?php
  2. /**
  3.  * PrestaShop 1.7.8.x – Активни продукти репорт (Bootstrap 5 + DataTables)
  4.  * Единичен файл (не модул). Постави го в корена на магазина или подпапка.
  5.  * Съвместим с PHP 7.3.
  6.  * Автор: Код програмиране
  7.  */
  8.  
  9. // ---------- НАМИРАНЕ НА /config/config.inc.php (без твърди пътища) ----------
  10. function ps_find_config() {
  11.     $dir = __DIR__;
  12.     for ($i = 0; $i < 8; $i++) {
  13.         $candidate = rtrim($dir, '/').'/config/config.inc.php';
  14.         if (is_file($candidate)) return $candidate;
  15.         $parent = dirname($dir);
  16.         if ($parent === $dir) break;
  17.         $dir = $parent;
  18.     }
  19.     return '';
  20. }
  21. $cfg = ps_find_config();
  22. if (!$cfg) { http_response_code(500); echo 'Не намирам /config/config.inc.php.'; exit; }
  23. require_once $cfg;
  24.  
  25. // Допълнителни include-и (някои инсталации изискват това)
  26. if (defined('_PS_ROOT_DIR_')) {
  27.     $extra = array(
  28.         _PS_ROOT_DIR_.'/config/defines.inc.php',
  29.         _PS_ROOT_DIR_.'/config/autoload.php',
  30.         _PS_ROOT_DIR_.'/config/bootstrap.php',
  31.         _PS_ROOT_DIR_.'/config/alias.php',
  32.         _PS_ROOT_DIR_.'/config/defines_uri.inc.php',
  33.         _PS_ROOT_DIR_.'/config/ini_sets.inc.php',
  34.     );
  35.     foreach ($extra as $ex) { if (is_file($ex)) require_once $ex; }
  36. }
  37.  
  38. // ---------- ИНИЦИАЛИЗАЦИЯ НА PRESTASHOP ----------
  39. try {
  40.     if (!defined('_PS_VERSION_')) {
  41.         require_once _PS_ROOT_DIR_.'/config/config.inc.php';
  42.     }
  43.    
  44.     // Инициализиране на контекста
  45.     if (class_exists('Context')) {
  46.         $context = Context::getContext();
  47.         if (!$context->controller) {
  48.             $context->controller = new FrontController();
  49.             $context->controller->init();
  50.         }
  51.     }
  52.    
  53.     // Гарантиране на shop контекст
  54.     if (Shop::isFeatureActive()) {
  55.         if (!Shop::getContext()) {
  56.             Shop::setContext(Shop::CONTEXT_SHOP, (int)Configuration::get('PS_SHOP_DEFAULT'));
  57.         }
  58.     }
  59.    
  60. } catch (Exception $e) {
  61.     http_response_code(500);
  62.     header('Content-Type: application/json; charset=utf-8');
  63.     echo json_encode(array('error' => 'PrestaShop initialization failed: ' . $e->getMessage()));
  64.     exit;
  65. }
  66.  
  67. // ---------- ГАРАНТИРАН КОНТЕКСТ ----------
  68. $context = Context::getContext();
  69.  
  70. // Shop
  71. if (!$context->shop || !(int)$context->shop->id) {
  72.     $idShop = (int)Configuration::get('PS_SHOP_DEFAULT');
  73.     if ($idShop) {
  74.         $context->shop = new Shop($idShop);
  75.         Shop::setContext(Shop::CONTEXT_SHOP, $idShop);
  76.     }
  77. }
  78. // Language
  79. if (!$context->language || !(int)$context->language->id) {
  80.     $idLang = (int)Configuration::get('PS_LANG_DEFAULT');
  81.     if ($idLang) $context->language = new Language($idLang);
  82. }
  83. // Currency
  84. if (!$context->currency || !(int)$context->currency->id) {
  85.     $idCur = (int)Configuration::get('PS_CURRENCY_DEFAULT');
  86.     if ($idCur) $context->currency = new Currency($idCur);
  87. }
  88. // Country
  89. if (!$context->country || !(int)$context->country->id) {
  90.     $idCountry = (int)Configuration::get('PS_COUNTRY_DEFAULT');
  91.     if ($idCountry) $context->country = new Country($idCountry);
  92. }
  93.  
  94. $link = $context->link;
  95.  
  96. // ---------- ПОМОЩНИ ФУНКЦИИ ----------
  97. function ps_get_active_languages() {
  98.     static $langs = null;
  99.     if ($langs === null) {
  100.         $langs = Language::getLanguages(true, Context::getContext()->shop->id);
  101.     }
  102.     return $langs;
  103. }
  104.  
  105. function ps_currency() {
  106.     static $cur = null;
  107.     if ($cur === null) {
  108.         $defaultId = (int)Configuration::get('PS_CURRENCY_DEFAULT');
  109.         $cur = new Currency($defaultId);
  110.     }
  111.     return $cur;
  112. }
  113.  
  114. // Активни продукти (ID desc)
  115. function ps_fetch_active_products($offset=0, $limit=100000) {
  116.     $sql = (new DbQuery())
  117.         ->select('p.id_product, p.reference')
  118.         ->from('product', 'p')
  119.         ->innerJoin('product_shop', 'ps', 'p.id_product = ps.id_product AND ps.id_shop = '.(int)Context::getContext()->shop->id)
  120.         ->where('ps.active = 1')
  121.         ->orderBy('p.id_product DESC')
  122.         ->limit((int)$limit, (int)$offset);
  123.    
  124.     $rows = Db::getInstance()->executeS($sql);
  125.     return $rows ? $rows : array();
  126. }
  127.  
  128. // Имена и link_rewrite за всички активни езици
  129. function ps_fetch_names_for_product($id_product) {
  130.     $langs = ps_get_active_languages();
  131.     $ids = array();
  132.     foreach ($langs as $l) $ids[] = (int)$l['id_lang'];
  133.     if (!$ids) return array();
  134.  
  135.     $sql = (new DbQuery())
  136.         ->select('pl.id_lang, pl.name, pl.link_rewrite')
  137.         ->from('product_lang', 'pl')
  138.         ->where('pl.id_product='.(int)$id_product)
  139.         ->where('pl.id_shop='.(int)Context::getContext()->shop->id)
  140.         ->where('pl.id_lang IN ('.implode(',', $ids).')');
  141.    
  142.     $rows = Db::getInstance()->executeS($sql);
  143.     $out = array();
  144.     if ($rows) {
  145.         foreach ($rows as $r) {
  146.             $out[(int)$r['id_lang']] = array(
  147.                 'name' => $r['name'],
  148.                 'link_rewrite' => $r['link_rewrite'],
  149.             );
  150.         }
  151.     }
  152.     return $out;
  153. }
  154.  
  155. // id на кавър изображението
  156. function ps_cover_image_id($id_product) {
  157.     $cover = Image::getCover($id_product);
  158.     if ($cover && !empty($cover['id_image'])) return (int)$cover['id_image'];
  159.     return null;
  160. }
  161.  
  162. // URL за миниатюра + URL към продукта
  163. function ps_image_and_product_links($id_product, $namesByLang, $link) {
  164.     $langs = ps_get_active_languages();
  165.     $firstLangId = (int)$langs[0]['id_lang'];
  166.     $linkRewrite = isset($namesByLang[$firstLangId]['link_rewrite']) ? $namesByLang[$firstLangId]['link_rewrite'] : 'product';
  167.  
  168.     $coverId = ps_cover_image_id($id_product);
  169.  
  170.     // Определяне на типа изображение
  171.     $thumbType = 'home_default';
  172.     if (class_exists('ImageType')) {
  173.         if (method_exists('ImageType','getFormattedName')) {
  174.             $thumbType = ImageType::getFormattedName('home');
  175.         } elseif (method_exists('ImageType','getFormatedName')) {
  176.             $thumbType = ImageType::getFormatedName('home');
  177.         }
  178.     }
  179.  
  180.     $imgUrl = $coverId ? $link->getImageLink($linkRewrite, $coverId, $thumbType) : null;
  181.     $productUrl = $link->getProductLink($id_product, $linkRewrite, null, null, $firstLangId);
  182.    
  183.     return array($imgUrl, $productUrl);
  184. }
  185.  
  186. // Наличности по комбинации
  187. function ps_sizes_and_qty($id_product) {
  188.     $langs = ps_get_active_languages();
  189.     $id_lang = (int)$langs[0]['id_lang'];
  190.     $prod = new Product($id_product, false, $id_lang, Context::getContext()->shop->id);
  191.     $combs = $prod->getAttributeCombinations($id_lang);
  192.     if (!$combs) $combs = array();
  193.  
  194.     $byAttr = array();
  195.     foreach ($combs as $c) {
  196.         $ipa = (int)$c['id_product_attribute'];
  197.         if (!isset($byAttr[$ipa])) $byAttr[$ipa] = array('attrs'=>array(), 'ipa'=>$ipa);
  198.         $gname = isset($c['group_name']) ? mb_strtolower($c['group_name']) : '';
  199.         $aname = isset($c['attribute_name']) ? $c['attribute_name'] : '';
  200.         $byAttr[$ipa]['attrs'][] = array('group'=>$gname, 'name'=>$aname);
  201.     }
  202.  
  203.     $preferSize = array();
  204.     foreach ($byAttr as $ipa => $data) {
  205.         foreach ($data['attrs'] as $a) {
  206.             if (preg_match('/(size|размер)/iu', $a['group'])) { $preferSize[$ipa] = true; break; }
  207.         }
  208.     }
  209.  
  210.     $rows = array();
  211.     foreach ($byAttr as $ipa => $data) {
  212.         $qty = (int)StockAvailable::getQuantityAvailableByProduct($id_product, $ipa, (int)Context::getContext()->shop->id);
  213.         $sizeLabel = null;
  214.         if (!empty($preferSize[$ipa])) {
  215.             foreach ($data['attrs'] as $a) {
  216.                 if (preg_match('/(size|размер)/iu', $a['group'])) { $sizeLabel = $a['name']; break; }
  217.             }
  218.         }
  219.         if ($sizeLabel === null) {
  220.             $parts = array(); foreach ($data['attrs'] as $a) { $parts[] = $a['name']; }
  221.             $sizeLabel = implode(' / ', $parts);
  222.         }
  223.         $rows[] = array('label'=>$sizeLabel, 'qty'=>$qty);
  224.     }
  225.  
  226.     $withStock = array(); foreach ($rows as $r) { if ($r['qty'] > 0) $withStock[] = $r; }
  227.     return $withStock ? $withStock : $rows;
  228. }
  229.  
  230. // Общо наличност
  231. function ps_total_qty($id_product) {
  232.     return (int)StockAvailable::getQuantityAvailableByProduct($id_product, 0, (int)Context::getContext()->shop->id);
  233. }
  234.  
  235. // Цени
  236. function ps_prices($id_product) {
  237.     $ctx = Context::getContext();
  238.     $idShop = (int)$ctx->shop->id;
  239.     $idCur  = (int)$ctx->currency->id;
  240.  
  241.     // Базова цена от product_shop
  242.     $q = new DbQuery();
  243.     $q->select('ps.price')
  244.       ->from('product_shop', 'ps')
  245.       ->where('ps.id_product='.(int)$id_product)
  246.       ->where('ps.id_shop='.(int)$idShop);
  247.     $row = Db::getInstance()->getRow($q);
  248.     $base = $row ? (float)$row['price'] : 0.0;
  249.  
  250.     // Търсене на специфични цени
  251.     $now = date('Y-m-d H:i:s');
  252.     $spQ = new DbQuery();
  253.     $spQ->select('reduction, reduction_type')
  254.         ->from('specific_price', 'sp')
  255.         ->where('sp.id_product='.(int)$id_product)
  256.         ->where('sp.id_shop IN (0,'.(int)$idShop.')')
  257.         ->where('sp.id_currency IN (0,'.(int)$idCur.')')
  258.         ->where('sp.id_country IN (0,'.(int)$ctx->country->id.')')
  259.         ->where('sp.id_group = 0')
  260.         ->where('sp.id_customer = 0')
  261.         ->where('(sp.from = "0000-00-00 00:00:00" OR sp.from <= "'.$now.'")')
  262.         ->where('(sp.to   = "0000-00-00 00:00:00" OR sp.to   >= "'.$now.'")')
  263.         ->orderBy('sp.id_specific_price DESC');
  264.     $sp = Db::getInstance()->getRow($spQ);
  265.  
  266.     $current = $base;
  267.     if ($sp && (float)$sp['reduction'] > 0) {
  268.         if ($sp['reduction_type'] === 'percentage') {
  269.             $current = $base * (1.0 - (float)$sp['reduction']);
  270.         } else { // amount
  271.             $current = $base - (float)$sp['reduction'];
  272.         }
  273.         if ($current < 0) $current = 0.0;
  274.     }
  275.  
  276.     // ДОБАВЕТЕ ТУК УМНОЖЕНИЕТО С 1.2 ЗА ДДС
  277.     $base_with_vat = $base * 1.2;
  278.     $current_with_vat = $current * 1.2;
  279.  
  280.     return array((float)$base, (float)$current);
  281. }
  282.  
  283. // Форматиране на цена
  284. function ps_fmt_price($price) {
  285.     // Добавяне на ДДС
  286.     $price_with_vat = $price * 1.2;    
  287.     return Tools::displayPrice($price, ps_currency());
  288. }
  289.  
  290. // Събиране на данните
  291. function ps_collect_data() {
  292.     $products = ps_fetch_active_products(0, 100000);
  293.     $langs = ps_get_active_languages();
  294.     $out = array();
  295.  
  296.     foreach ($products as $p) {
  297.         $id  = (int)$p['id_product'];
  298.         $ref = $p['reference'];
  299.  
  300.         $names = ps_fetch_names_for_product($id);
  301.         list($imgUrl, $pUrl) = ps_image_and_product_links($id, $names, Context::getContext()->link);
  302.  
  303.         // Имена за всички активни езици
  304.         $nameParts = array();
  305.         foreach ($langs as $l) {
  306.             $id_lang = (int)$l['id_lang'];
  307.             $iso = $l['iso_code'];
  308.             $nm = isset($names[$id_lang]['name']) ? $names[$id_lang]['name'] : '';
  309.             if ($nm !== '') { $nameParts[] = '<p>['.$iso.'] '.$nm.'</p>'; }
  310.         }
  311.         $namesAll = implode("\n", $nameParts);
  312.  
  313.         // Размери + количества
  314.         $sizes = ps_sizes_and_qty($id);
  315.         $sizeParts = array();
  316.         foreach ($sizes as $s) {
  317.             $label = isset($s['label']) ? $s['label'] : '';
  318.             $qty   = isset($s['qty']) ? (int)$s['qty'] : 0;
  319.             $sizeParts[] = $label . ': ' . $qty;
  320.         }
  321.         $sizesStr = implode("\n", $sizeParts);
  322.  
  323.         $totalQty = ps_total_qty($id);
  324.         list($base, $curr) = ps_prices($id);
  325.  
  326.         // ДОБАВЕТЕ ДДС ТУК ДИРЕКТНО
  327.         $base_with_vat = $base * 1.2;
  328.         $curr_with_vat = $curr * 1.2;
  329.  
  330.         $out[] = array(
  331.             'id'        => $id,
  332.             'reference' => $ref,
  333.             'image'     => $imgUrl,
  334.             'plink'     => $pUrl,
  335.             'names'     => $namesAll,
  336.             'sizes'     => $sizesStr,
  337.             'total'     => $totalQty,
  338.             'base'      => ps_fmt_price($base * 1.2), //Това умножение по 1,2 се прави за да показва цената с ДДС
  339.             'current'   => ps_fmt_price($curr * 1.2), //Това умножение по 1,2 се прави за да показва цената с ДДС
  340.             'has_disc'  => ($curr < $base) ? 1 : 0,
  341.             //'disc_percent' => ($base > 0 && $curr < $base) ? (int)round((($base - $curr) / $base) * 100) : 0,
  342.             'disc_percent' => ($base > 0 && $curr < $base)
  343.         ? (int) round((($base - $curr) / $base) * 100)
  344.         : 0,
  345.         );
  346.     }
  347.     return $out;
  348. }
  349.  
  350. // ---------- РЕЖИМИ (AJAX/EXPORT/VIEW) ----------
  351. if (isset($_GET['action']) && $_GET['action'] === 'data') {
  352.     header('Content-Type: application/json; charset=utf-8');
  353.     try {
  354.         $data = ps_collect_data();
  355.         echo json_encode(array('data'=>$data), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
  356.     } catch (Exception $e) {
  357.         echo json_encode(array('error'=>'Грешка при събиране на данни: ' . $e->getMessage()));
  358.     }
  359.     exit;
  360. }
  361.  
  362. // Експорт към Excel (CSV UTF-8 BOM)
  363. if (isset($_GET['export']) && $_GET['export'] === '1') {
  364.     try {
  365.         $rows = ps_collect_data();
  366.         $filename = 'products_export_' . date('Ymd_His') . '.csv';
  367.         header('Content-Type: text/csv; charset=utf-8');
  368.         header('Content-Disposition: attachment; filename="' . $filename . '"');
  369.         echo "\xEF\xBB\xBF";
  370.         $out = fopen('php://output', 'w');
  371.         fputcsv($out, array(
  372.             'ID','Reference','Product URL','Image URL','Names (all languages)',
  373.             'Sizes (qty)','Total Qty','Base Price','Current Price','Discount %',
  374.         ), ';');
  375.         foreach ($rows as $r) {
  376.             fputcsv($out, array(
  377.                 $r['id'],
  378.                 $r['reference'],
  379.                 $r['plink'],
  380.                 $r['image'],
  381.                 preg_replace("/\r?\n/", ' | ', $r['names']),
  382.                 preg_replace("/\r?\n/", ' | ', $r['sizes']),
  383.                 $r['total'],
  384.                 $r['base'],
  385.                 $r['current'],
  386.                 ($r['disc_percent'] > 0 ? ('-' . $r['disc_percent'] . '%') : ''),
  387.             ), ';');
  388.         }
  389.         fclose($out);
  390.     } catch (Exception $e) {
  391.         header('Content-Type: text/plain; charset=utf-8');
  392.         echo 'Грешка при експорт: ' . $e->getMessage();
  393.     }
  394.     exit;
  395. }
  396.  
  397. ?>
  398. <!doctype html>
  399. <html lang="bg">
  400. <head>
  401.   <meta charset="utf-8">
  402.   <title>Активни продукти – репорт</title>
  403.   <meta name="viewport" content="width=device-width, initial-scale=1">
  404.  
  405.   <!-- Bootstrap 5 -->
  406.   <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  407.  
  408.   <!-- DataTables (Bootstrap 5 theme) -->
  409.   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/dataTables.bootstrap5.min.css">
  410.   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/responsive.bootstrap5.min.css">
  411.  
  412.   <style>
  413.     body { background:#f8f9fa; padding-top: 20px; }
  414.     .price-current { font-weight: 600; }
  415.     .price-base { text-decoration: line-through; opacity: .7; margin-right:.5rem; }
  416.     td pre { white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: .85rem; margin: 0; }
  417.     .thumb { width:56px; height:56px; object-fit:cover; border-radius:.5rem; }
  418.     .card { border: none; box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); }
  419.     .table td { vertical-align: middle; }
  420.   </style>
  421. </head>
  422. <body>
  423. <div class="container-fluid py-4">
  424.   <div class="d-flex align-items-center justify-content-between mb-4">
  425.     <h1 class="h3 mb-0 text-primary">Активни продукти / <a href="/check-products.php" class="btn btn-warning" >Неактивни с наличност за продажба!</a></h1>
  426.     <div class="d-flex gap-2">
  427.       <a href="?export=1" class="btn btn-success">
  428.         📊 Експорт в Excel (CSV)
  429.       </a>
  430.     </div>
  431.   </div>
  432.  
  433.   <!-- Лоудър -->
  434.   <div id="loader" class="card shadow-sm mb-4">
  435.     <div class="card-body d-flex align-items-center gap-3 py-4">
  436.       <div class="spinner-border text-primary" role="status" aria-hidden="true"></div>
  437.       <div>
  438.         <div class="fw-semibold">Зареждане на данни…</div>
  439.         <div class="text-muted small">Може да отнеме малко време при много продукти.</div>
  440.       </div>
  441.     </div>
  442.   </div>
  443.  
  444.   <div class="card shadow-sm">
  445.     <div class="card-body p-0">
  446.       <table id="products" class="table table-striped table-hover align-middle" style="width:100%">
  447.         <thead class="table-light">
  448.         <tr>
  449.           <th width="80">ID</th>
  450.           <th width="100">Ref</th>
  451.           <th width="70">Снимка</th>
  452.           <th>Име (всички езици)</th>
  453.           <th>Размери (наличности)</th>
  454.           <th width="120">Общо наличност</th>
  455.           <th width="150">Цена (с ДДС)</th>
  456.         </tr>
  457.         </thead>
  458.         <tbody></tbody>
  459.       </table>
  460.     </div>
  461.   </div>
  462. </div>
  463.  
  464. <!-- JS -->
  465. <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
  466. <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  467.  
  468. <!-- DataTables -->
  469. <script src="https://cdn.jsdelivr.net/npm/[email protected]/js/jquery.dataTables.min.js"></script>
  470. <script src="https://cdn.jsdelivr.net/npm/[email protected]/js/dataTables.bootstrap5.min.js"></script>
  471. <script src="https://cdn.jsdelivr.net/npm/[email protected]/js/dataTables.responsive.min.js"></script>
  472. <script src="https://cdn.jsdelivr.net/npm/[email protected]/js/responsive.bootstrap5.min.js"></script>
  473.  
  474. <script>
  475. (function() {
  476.   const tableEl = $('#products');
  477.   const loaderEl = $('#loader');
  478.  
  479.   fetch('?action=data')
  480.     .then(r => {
  481.       if (!r.ok) throw new Error('Network error: ' + r.status);
  482.       return r.json();
  483.     })
  484.     .then(json => {
  485.       if (json.error) {
  486.         throw new Error(json.error);
  487.       }
  488.      
  489.       loaderEl.remove();
  490.       const data = json.data || [];
  491.      
  492.       if (data.length === 0) {
  493.         loaderEl.find('.card-body').html(
  494.           '<div class="alert alert-warning mb-0">Няма активни продукти за показване.</div>'
  495.         );
  496.         return;
  497.       }
  498.      
  499.       const table = tableEl.DataTable({
  500.         data: data,
  501.         responsive: true,
  502.         deferRender: true,
  503.         pageLength: 500,
  504.         lengthMenu: [100, 250, 500, 1000],
  505.         order: [[0, 'desc']],
  506.         columns: [
  507.           {
  508.             data: 'id',
  509.             className: 'fw-bold text-center'
  510.           },
  511.           {
  512.             data: 'reference',
  513.             defaultContent: '-',
  514.             className: 'text-center'
  515.           },
  516.           {
  517.             data: null,
  518.             orderable: false,
  519.             searchable: false,
  520.             className: 'text-center',
  521.             render: function(row) {
  522.               if (!row.image) return '<span class="text-muted">Няма</span>';
  523.               const href = row.plink || '#';
  524.               return '<a href="' + href + '" target="_blank" rel="noopener" title="Отвори продукта">' +
  525.                        '<img src="' + row.image + '" class="thumb" alt="thumb" onerror="this.style.display=\'none\'">' +
  526.                      '</a>';
  527.             }
  528.           },
  529.           {
  530.             data: 'names',
  531.             render: function(data) {
  532.               return data ? '<pre>' + data.replace(/\n/g,'\n') + '</pre>' : '<span class="text-muted">Няма име</span>';
  533.             }
  534.           },
  535.           {
  536.             data: 'sizes',
  537.             render: function(data) {
  538.               return data ? '<pre class="text-small">' + data.replace(/\n/g,'\n') + '</pre>' : '<span class="text-muted">Няма варианти</span>';
  539.             }
  540.           },
  541.           {
  542.             data: 'total',
  543.             className: 'text-center fw-bold',
  544.             render: function(data) {
  545.               const qty = parseInt(data) || 0;
  546.               const cls = qty > 0 ? 'text-success' : 'text-danger';
  547.               return '<span class="' + cls + '">' + qty + '</span>';
  548.             }
  549.           },
  550.           {
  551.             data: null,
  552.            
  553.   className: 'text-end',
  554.   render: function(row) {
  555.     if (row.has_disc && row.base !== row.current) {
  556.       var badge = (typeof row.disc_percent !== 'undefined' && row.disc_percent > 0)
  557.         ? ' <span class="badge bg-success ms-2" style="padding:10px; width:100px; font-size:20pt;">-' + row.disc_percent + '%</span>'
  558.         : '';
  559.       return '<div>' +
  560.         '<span class="price-base text-muted text-decoration-line-through me-2">' + row.base + '</span>' +
  561.         '<span class="price-current text-danger" style="font-size:25pt;font-weight:bold;">' + row.current + '</span>' +
  562.         badge +
  563.       '</div>';
  564.     }
  565.     return '<span class="price-current">' + row.current + '</span>';
  566.   }
  567. }
  568.         ],
  569.         dom: '<"row mb-3"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6"f>><"row"<"col-sm-12"tr>><"row mt-3"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>',
  570.         language: {
  571.           url: 'https://cdn.datatables.net/plug-ins/1.13.10/i18n/bg.json',
  572.           search: "Търсене:",
  573.           lengthMenu: "Покажи _MENU_ записа",
  574.           info: "Показване на _START_ до _END_ от _TOTAL_ записа",
  575.           paginate: {
  576.             first: "Първа",
  577.             last: "Последна",
  578.             next: "Следваща",
  579.             previous: "Предишна"
  580.           }
  581.         }
  582.       });
  583.     })
  584.     .catch(err => {
  585.       console.error('Error:', err);
  586.       loaderEl.find('.card-body').html(
  587.         '<div class="alert alert-danger mb-0">Грешка при зареждане на данните: ' + err.message + '</div>'
  588.       );
  589.     });
  590. })();
  591. </script>
  592. </body>
  593. </html>
Tags: php Prestashop
Advertisement
Add Comment
Please, Sign In to add comment