first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
<?php
namespace KupShop\PriceHistoryBundle\Admin\Actions;
use KupShop\AdminBundle\Admin\Actions\AbstractAction;
use KupShop\AdminBundle\Admin\Actions\ActionResult;
use KupShop\AdminBundle\Admin\Actions\IAction;
class ResetPriceForDiscountAction extends AbstractAction implements IAction
{
public function getTypes(): array
{
return ['products'];
}
public function getName(): string
{
return 'Resetovat cenu pro slevu';
}
public function showInMassEdit()
{
return true;
}
public function execute(&$data, array $config, string $type): ActionResult
{
$resetPriceIncludingDiscount = ($data['resetPriceIncludingDiscount'] ?? false) == '1';
$selectedValues = $data['variationsValues'] ?? [];
$params = [$this->getId()];
$joins = '';
$where = '';
if (!empty($selectedValues)) {
$placeholders = implode(',', array_fill(0, count($selectedValues), '?'));
$params = array_merge($params, $selectedValues);
$joins .= 'JOIN products_variations_combination pvc ON pvc.id_variation = pv.id';
$where .= ' AND pvc.id_value IN ('.$placeholders.')';
}
$priceData = sqlFetch(sqlQuery('SELECT '.$this->getPriceCalculation('p', $resetPriceIncludingDiscount).', price_for_discount FROM products p WHERE id = :product_id', ['product_id' => $this->getId()]));
if ($priceData['price'] > $priceData['price_for_discount']) {
return new ActionResult(false, translate('reserCPSComment', 'products'));
}
// pridat do historie zaznam s aktualni cenou s up=1, aby cron prepocital CPS
// bez toho noveho zaznamu o "zvyseni ceny" se CPS vracela na puvodni vyssi hodnotu
sqlQuery('INSERT INTO price_history (id_product, price, last, date_change, up)
SELECT p.id, '.$this->getPriceCalculation('p', $resetPriceIncludingDiscount).', 1, NOW(), 1
FROM products p
WHERE p.id = :product_id',
['product_id' => $this->getId()]);
sqlQuery('INSERT INTO price_history (id_product, id_variation, price, last, date_change, up)
SELECT pv.id_product, pv.id, pv.price * (1 - p.discount / 100), 1, NOW(), 1
FROM products_variations pv
JOIN products p ON p.id = pv.id_product AND p.figure = "Y"
'.$joins.'
WHERE p.id = ?
AND pv.figure = "Y"
AND pv.price IS NOT NULL
AND pv.price < pv.price_for_discount'.$where, $params
);
// product
sqlQuery('UPDATE products p SET price_for_discount = '.$this->getPriceCalculation('p', $resetPriceIncludingDiscount).' WHERE id = :product_id', ['product_id' => $this->getId()]);
// variants
sqlQuery('UPDATE products_variations pv
'.$joins.'
SET pv.price_for_discount = pv.price
WHERE pv.id_product = ?
AND pv.price IS NOT NULL
AND pv.price < pv.price_for_discount
'.$where, $params
);
if (findModule(\Modules::PRICELISTS) && ($data['resetPriceListPriceForDiscount'] ?? false)) {
sqlQuery('UPDATE pricelists_products pp
JOIN pricelists p on p.id = pp.id_pricelist
SET pp.price_for_discount = '.$this->getPriceCalculation('pp', $resetPriceIncludingDiscount).'
WHERE pp.id_product = :product_id AND pp.price IS NOT null AND '.$this->getPriceCalculation('pp', $resetPriceIncludingDiscount).' < pp.price_for_discount AND p.price_history = 1',
['product_id' => $this->getId()]
);
}
$this->updateHistoryTag($priceData['price']);
return new ActionResult(true, 'Cena pro slevu resetována.');
}
private function updateHistoryTag($price)
{
$priceListCond = '';
if (findModule(\Modules::PRICELISTS)) {
$priceListCond = 'AND id_pricelist IS NULL';
}
// Reset tagů v historii
sqlQuery("UPDATE price_history SET last = 0 WHERE id_product = :product_id AND last = -1 {$priceListCond}", ['product_id' => $this->getId()]);
sqlQuery("UPDATE price_history SET last = -1 WHERE id = (
SELECT id FROM price_history
WHERE id_product = :product_id AND id_variation IS NULL AND price = :default_price {$priceListCond} AND last != 1
ORDER BY date_change DESC LIMIT 1)
", ['product_id' => $this->getId(), 'default_price' => $price]);
}
private function getPriceCalculation(string $alias = 'p', ?bool $includingDiscount = false): string
{
return $includingDiscount ? $alias.'.price * (1 - '.$alias.'.discount / 100)' : $alias.'.price';
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace KupShop\PriceHistoryBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
class PriceHistoryTab extends WindowTab
{
protected $title = 'flapPriceHistory';
protected $template = 'PriceHistoryTab.tpl';
public static function getTypes()
{
return [
'products' => 1,
];
}
}

View File

@@ -0,0 +1,5 @@
<?php
$txt_str['priceHistoryTab'] = [
'flapPriceHistory' => 'Historie cen',
];

View File

@@ -0,0 +1,5 @@
<?php
$txt_str['priceHistoryTab'] = [
'flapPriceHistory' => 'Price history',
];

View File

@@ -0,0 +1,128 @@
<?php
namespace KupShop\PriceHistoryBundle\Admin\lists;
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
use Query\Operator;
use Query\QueryBuilder;
class PriceHistoryList extends BaseList
{
protected $tableDef = [
'id' => '',
'fields' => [
'' => ['field' => 'up', 'render' => 'renderUpDown', 'size' => '70px'],
'Cena' => ['field' => 'price', 'render' => 'renderPriceHistoryPrice', 'label' => 'labelPricePreferred'],
' ' => ['field' => 'last', 'render' => 'renderLast', 'size' => '70px'],
'Datum změny' => ['field' => 'date_change', 'render' => 'renderDate'],
],
];
protected $orderParam = [
'sort' => 'Datum změny',
'direction' => 'DESC',
];
public function customizeTableDef($tableDef)
{
$tableDef = parent::customizeTableDef($tableDef);
if (findModule(\Modules::PRODUCTS_VARIATIONS)) {
$tableDef['fields']['Varianta'] = [
'field' => 'variation_title',
'spec' => function (QueryBuilder $qb) {
$qb->leftJoin('ph', 'products_variations', 'pv', 'pv.id = ph.id_variation');
$qb->addSelect('pv.title AS variation_title');
},
'visible' => 'Y',
];
}
if (findModule(\Modules::PRICELISTS)) {
$tableDef['fields']['Ceník'] = [
'field' => 'pricelist',
'spec' => function (QueryBuilder $qb) {
$qb->addSelect('pl.id as id_pricelist, pl.name as pricelist, pl.currency as pricelist_currency')
->leftJoin('ph', 'pricelists', 'pl', 'pl.id = ph.id_pricelist');
},
'render' => 'renderPriceList',
'visible' => 'Y',
];
}
return $tableDef;
}
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('ph.*, p.vat')
->from('price_history', 'ph')
->join('ph', 'products', 'p', 'p.id = ph.id_product')
->andWhere(Operator::equals(['ph.id_product' => getVal('id_product')]))
->orderBy('ph.id_variation')
->addOrderBy('ph.date_change');
return $qb;
}
public function renderPriceList($values): HTML
{
if (!$values['pricelist']) {
return HTML::create('span')->text('Základní ceník');
}
return HTML::create('a')
->attr('href', 'javascript:nw(\'pricelist\', '.$values['id_pricelist'].')')
->text($values['pricelist']);
}
public function renderPriceHistoryPrice($values, $column): array
{
$currency = $values['pricelist_currency'] ?? null;
if (empty($currency)) {
$currency = Contexts::get(CurrencyContext::class)->getDefaultId();
}
$values['currency'] = $currency;
return ServiceContainer::getService(ContextManager::class)
->activateContexts([CurrencyContext::class => $currency], function () use ($values, $column) {
if (\Settings::getDefault()->prod_prefer_price_vat == 'N') {
return $this->renderPrice($values, $column);
}
return $this->renderPriceVAT($values, $column);
});
}
public function renderLast($values, $column)
{
if ($values['last'] == -1) {
return HTML::create('abbr')
->text('CPS')
->attr('title', 'Cena pro slevu')
->end();
}
return '';
}
public function renderUpDown($values, $column)
{
$up = $values['up'];
$class = ($up == 1 ? 'list-no bi bi-arrow-up' : 'list-yes bi bi-arrow-down');
return HTML::create('span')
->class($class)
->tag('i')
->text($up == 1 ? 'up' : 'down')
->end();
}
}
return PriceHistoryList::class;

View File

@@ -0,0 +1,16 @@
<div id="flapPriceHistory" class="tab-pane fade in boxFlex box">
<h1 class="h4 main-panel-title col-md-12">Historie cen</h1>
<div class="row col-md-12">
<div class="col-md-12">
{$price_for_discount = $body.data.price_for_discount}
{if $price_for_discount && $body.data.priceWithVat}
{$price_for_discount = $price_for_discount->addVat(getVat($body.data.vat))}
{/if}
{$default_currency = $dbcfg.currency_code}
{'priceForDiscount'|translate}: <label>{if $price_for_discount}{$price_for_discount|format_price:"currency=$default_currency"}{/if}</label>
</div>
</div>
<div class="row boxFlex box col-md-12">
<iframe class='on-demand boxFlex' src="" data-src="launch.php?s=list.php&amp;type=priceHistory&amp;id_product={$body.data.id}&amp;sortable='true'"></iframe>
</div>
</div>

View File

@@ -0,0 +1,25 @@
{extends "[AdminBundle]actions/baseAction.tpl"}
{block actionContent}
<p class="text-center m-b-3"><strong>{'resetCPSConfirm'|translate nofilter}</strong></p>
<h1 class="h4 main-panel-title">{'reserCPSComment'|translate} <span data-attachment="" class="badge badge-default pull-right"></span></h1>
{if $module.PRICELISTS}
<div class="checkbox">
<input type="checkbox" class="check" value="1" checked name="data[resetPriceListPriceForDiscount]" id="resetPriceListPriceForDiscount"
{if $dbcfg.resetPriceListPriceForDiscount == 'Y'}checked{/if}>
<label for="resetPriceListPriceForDiscount"><strong>{'resetPriceListPriceForDiscount'|translate}</strong></label><br/>
<input type="checkbox" class="check" value="1" checked name="data[resetPriceIncludingDiscount]" id="resetPriceIncludingDiscount">
<label for="resetPriceIncludingDiscount"><strong>{'resetPriceIncludingDiscount'|translate}</strong></label>
</div>
{/if}
{if $module.PRODUCTS_VARIATIONS}
<div class="col-md-12 m-t-1 m-b-1">
<p class="text-center m-b-1"><strong>{'resetPriceVariants'|translate}</strong></p>
<div class="infobox">
<p>{'resetPriceVariantsInfo'|translate}</p>
</div>
<select name="data[variationsValues][]" multiple class="selecter" data-label-select="" data-autocomplete="variationsValues"></select>
</div>
{/if}
{/block}