first commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Admin\lists;
|
||||
|
||||
class PricelistList extends \KupShop\AdminBundle\AdminList\BaseList
|
||||
{
|
||||
protected $tableDef = [
|
||||
'id' => 'id',
|
||||
'fields' => [
|
||||
'ID' => ['field' => 'id', 'size' => 0.1],
|
||||
'Název' => ['field' => 'name'],
|
||||
'Měna' => ['field' => 'currency'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $tableName = 'pricelists';
|
||||
protected ?string $tableAlias = 'pl';
|
||||
}
|
||||
|
||||
return PricelistList::class;
|
||||
130
bundles/KupShop/PricelistBundle/Admin/pricelist.php
Normal file
130
bundles/KupShop/PricelistBundle/Admin/pricelist.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
use Query\Operator;
|
||||
|
||||
class Pricelist extends Window
|
||||
{
|
||||
protected $nameField = 'name';
|
||||
|
||||
protected $tableName = 'pricelists';
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager */
|
||||
private $em;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->em = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService('doctrine.orm.entity_manager');
|
||||
}
|
||||
|
||||
public function get_vars()
|
||||
{
|
||||
$vars = parent::get_vars();
|
||||
|
||||
$pageVars = getVal('body', $vars);
|
||||
|
||||
$pageVars['currencies'] = $this->getCurrencies();
|
||||
|
||||
$this->unserializeCustomData($pageVars['data']);
|
||||
|
||||
$vars['body'] = $pageVars;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
$data = parent::getData();
|
||||
|
||||
if (!empty($data)) {
|
||||
if (findModule(Modules::PRICE_HISTORY)) {
|
||||
$data['price_history'] = ($data['price_history'] === 'Y' || $data['price_history'] == 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
$data['data'] = array_merge($this->getCustomData(), $data['data'] ?? []);
|
||||
|
||||
$this->serializeCustomData($data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
$currentPriceHistory = $this->getObject()['price_history'] ?? null;
|
||||
|
||||
$result = parent::handleUpdate();
|
||||
|
||||
if (findModule(Modules::PRICE_HISTORY) && $this->getID()) {
|
||||
$priceHistory = $this->getData()['price_history'] ?? false;
|
||||
// pokud se zmenila hodnota v poli price_history
|
||||
if ($currentPriceHistory != $priceHistory) {
|
||||
// historie cen byla zapnuta
|
||||
if ($priceHistory) {
|
||||
// Insertnu prvotni historii
|
||||
sqlQuery('
|
||||
INSERT INTO price_history (id_pricelist, id_product, id_variation, price, last, date_change, up)
|
||||
SELECT
|
||||
pp.id_pricelist,
|
||||
pp.id_product,
|
||||
pp.id_variation,
|
||||
pp.price,
|
||||
1,
|
||||
DATE_SUB(NOW(), INTERVAL 1 DAY),
|
||||
1
|
||||
FROM pricelists_products pp
|
||||
JOIN products p ON p.id = pp.id_product
|
||||
WHERE (pp.price > 0) AND p.figure = "Y" AND pp.id_pricelist = :priceListId;
|
||||
',
|
||||
['priceListId' => $this->getID()]);
|
||||
|
||||
// Nastavit CPS
|
||||
sqlQueryBuilder()
|
||||
->update('pricelists_products', 'pp')
|
||||
->join('pp', 'products', 'p', 'p.id = pp.id_product')
|
||||
->leftJoin('pp', 'products_variations', 'pv', 'pv.id = pp.id_variation')
|
||||
->set('pp.price_for_discount', 'COALESCE(pp.price, pv.price, p.price)')
|
||||
->where(Operator::equals(['pp.id_pricelist' => $this->getID()]))
|
||||
->execute();
|
||||
} else { // historie cen u ceniku byla vypnuta
|
||||
// Smazani CPS
|
||||
sqlQueryBuilder()
|
||||
->update('pricelists_products')
|
||||
->directValues(['price_for_discount' => null])
|
||||
->where(Operator::equals(['id_pricelist' => $this->getID()]))
|
||||
->execute();
|
||||
|
||||
// Smazani historie
|
||||
sqlQueryBuilder()
|
||||
->delete('price_history')
|
||||
->where(Operator::equals(['id_pricelist' => $this->getID()]))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getCurrencies()
|
||||
{
|
||||
$repository = $this->em->getRepository(\KupShop\I18nBundle\Entity\Currency::class);
|
||||
|
||||
return $repository->findAll();
|
||||
}
|
||||
|
||||
public function handleGeneratePricelistToken(): void
|
||||
{
|
||||
$token = bin2hex(random_bytes(16));
|
||||
|
||||
sqlQueryBuilder()
|
||||
->update('pricelists')
|
||||
->directValues(['token' => $token])
|
||||
->where(Operator::equals(['id' => $this->getID()]))
|
||||
->execute();
|
||||
|
||||
$this->returnOK();
|
||||
}
|
||||
}
|
||||
|
||||
$countries = new Pricelist();
|
||||
$countries->run();
|
||||
301
bundles/KupShop/PricelistBundle/Admin/products.pricelists.php
Normal file
301
bundles/KupShop/PricelistBundle/Admin/products.pricelists.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
|
||||
class ProductsPricelists extends Window
|
||||
{
|
||||
public $priceListWorker;
|
||||
public $vat;
|
||||
protected $template = 'window/products.pricelists.tpl';
|
||||
|
||||
private $productId;
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager */
|
||||
private $em;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->em = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService('doctrine.orm.entity_manager');
|
||||
$this->priceListWorker = \KupShop\KupShopBundle\Util\Compat\ServiceContainer::getService(\KupShop\PricelistBundle\Util\PriceListWorker::class);
|
||||
}
|
||||
|
||||
public function get_vars()
|
||||
{
|
||||
$dbcfg = Settings::getDefault();
|
||||
$vars = parent::get_vars();
|
||||
|
||||
$pageVars = getVal('body', $vars, []);
|
||||
|
||||
$pageVars['ID'] = $this->getProductId();
|
||||
$pageVars['defaultPricelist'] = $this->getDefaultPricelist();
|
||||
$pageVars['defaultCurrency'] = $this->getDefaultCurrency();
|
||||
$pageVars['pricelists'] = $this->getPricelists();
|
||||
$pageVars['data'] = $this->getPricelistData();
|
||||
$pageVars['variations'] = $this->getVariations();
|
||||
$pageVars['vat'] = $this->getVat();
|
||||
$pageVars['data']['showPricesWithVat'] = $dbcfg['prod_prefer_price_vat'] == 'Y' || $dbcfg['prod_prefer_price_vat'] == 'F';
|
||||
|
||||
$vars['body'] = $pageVars;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$data = parent::getData();
|
||||
unset($data['ID'], $data['showPricesWithVat']);
|
||||
if (!empty($data)) {
|
||||
$this->handleUpdatePricelists();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleUpdatePricelists()
|
||||
{
|
||||
$data = parent::getData();
|
||||
$showVat = $data['showPricesWithVat'];
|
||||
unset($data['ID']);
|
||||
unset($data['showPricesWithVat']);
|
||||
foreach ($data as $pricelistId => $options) {
|
||||
$options['showVat'] = $showVat;
|
||||
if ($pricelistId > 0) {
|
||||
$this->priceListWorker->updatePricelists($pricelistId, $options, $this->getProductId(), $this->getVat());
|
||||
// variations update
|
||||
if (isset($options['vars']) && $options['vars'] == 'true') {
|
||||
foreach ($options['priceVar'] as $varId => $price) {
|
||||
$discount = $options['discountVar'][$varId] ?? '';
|
||||
$varOptions = [
|
||||
'price' => $this->preparePrice($price),
|
||||
'discount' => $this->preparePrice($discount),
|
||||
'showVat' => $showVat,
|
||||
];
|
||||
$this->priceListWorker->updatePricelists($pricelistId, $varOptions, $this->getProductId(), $this->getVat(), $varId);
|
||||
}
|
||||
}
|
||||
// updating default pricelist
|
||||
} elseif ($pricelistId == 0) {
|
||||
$this->updateDefaultPricelist($options);
|
||||
// variations update in default pricelist
|
||||
if (isset($options['vars']) && $options['vars'] == 'true') {
|
||||
foreach ($options['priceVar'] as $varId => $price) {
|
||||
$this->updateDefaultVariations($varId, $this->preparePrice($price), $options['showVat']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->returnOK();
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
if (getVal('submitUpdatePricelists')) {
|
||||
$data = parent::getData();
|
||||
$showVat = $data['showPricesWithVat'];
|
||||
unset($data['ID']);
|
||||
unset($data['showPricesWithVat']);
|
||||
foreach ($data as $pricelistId => $options) {
|
||||
$options['showVat'] = $showVat;
|
||||
if ($pricelistId > 0) {
|
||||
$this->priceListWorker->updatePricelists($pricelistId, $options, $this->getProductId(), $this->getVat());
|
||||
// variations update
|
||||
if (isset($options['vars']) && $options['vars'] == 'true') {
|
||||
foreach ($options['priceVar'] as $varId => $price) {
|
||||
$this->priceListWorker->updatePricelists($pricelistId, ['price' => $this->preparePrice($price), 'showVat' => $showVat], $this->getProductId(), $this->getVat(), $varId);
|
||||
}
|
||||
}
|
||||
// updating default pricelist
|
||||
} elseif ($pricelistId == 0) {
|
||||
$this->updateDefaultPricelist($options);
|
||||
// variations update in default pricelist
|
||||
if (isset($options['vars']) && $options['vars'] == 'true') {
|
||||
foreach ($options['priceVar'] as $varId => $price) {
|
||||
$this->updateDefaultVariations($varId, $this->preparePrice($price), $options['showVat']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateDefaultPricelist($data)
|
||||
{
|
||||
// vat remove before saving to database
|
||||
if ($data['showVat'] == 'Y') {
|
||||
$data['price'] = $this->priceListWorker->removeVatFromPrice($this->preparePrice($data['price']), $this->getVat());
|
||||
}
|
||||
|
||||
$price = ['price' => $data['price'], 'discount' => $data['discount']];
|
||||
$where = ['id' => $this->getProductId()];
|
||||
$this->updateSQL('products', $price, $where);
|
||||
}
|
||||
|
||||
public function updateDefaultVariations($varId, $price, $showVat)
|
||||
{
|
||||
$price = Decimal::create($price);
|
||||
// vat remove before saving to database
|
||||
if ($showVat == 'Y') {
|
||||
$price = $this->priceListWorker->removeVatFromPrice($price, $this->getVat());
|
||||
}
|
||||
|
||||
if ($price->isZero()) {
|
||||
$price = null;
|
||||
}
|
||||
|
||||
$this->updateSQL('products_variations', ['price' => $price], ['id' => $varId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \KupShop\PricelistBundle\Entity\Pricelist $pricelist
|
||||
* @param Decimal $price
|
||||
*
|
||||
* @return Decimal
|
||||
*/
|
||||
public function recalcPriceFromCZK($pricelist, $price)
|
||||
{
|
||||
if ($pricelist) {
|
||||
if ($price) {
|
||||
return $price->div($pricelist->getCurrency()->getRate());
|
||||
}
|
||||
} else {
|
||||
if ($price) {
|
||||
$currency = $this->getDefaultCurrency();
|
||||
$rate = Decimal::create($currency['rate']);
|
||||
|
||||
return $price->div($rate);
|
||||
}
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \KupShop\I18nBundle\Entity\Currency $currency
|
||||
* @param string $price
|
||||
*
|
||||
* @return Decimal
|
||||
*/
|
||||
public function recalcPriceToCZK($currency, $price)
|
||||
{
|
||||
$price = Decimal::create($price);
|
||||
|
||||
return $price->mul($currency->getRate());
|
||||
}
|
||||
|
||||
public function getDefaultCurrency()
|
||||
{
|
||||
$dbcfg = Settings::getDefault();
|
||||
|
||||
return sqlQueryBuilder()
|
||||
->select('id as code, symbol, rate')
|
||||
->from('currencies')
|
||||
->where('id = ?')->setParameter(0, $dbcfg->currency)
|
||||
->setMaxResults(1)
|
||||
->execute()->fetch();
|
||||
}
|
||||
|
||||
public function getDefaultPricelist()
|
||||
{
|
||||
return sqlQueryBuilder()->select('price', 'discount')
|
||||
->from('products')
|
||||
->where('id = ?')->setParameter(0, $this->getProductId())
|
||||
->execute()->fetch();
|
||||
}
|
||||
|
||||
/* Variations data */
|
||||
public function getPricelistData()
|
||||
{
|
||||
$return = [];
|
||||
$repository = $this->em->getRepository(\KupShop\PricelistBundle\Entity\PricelistProduct::class);
|
||||
$data = $repository->findBy(['productId' => $this->getProductId()]);
|
||||
|
||||
/** @var \KupShop\PricelistBundle\Entity\PricelistProduct $value */
|
||||
foreach ($data as $value) {
|
||||
// pricelists
|
||||
if ($value->getVariationId() == null) {
|
||||
$return[$value->getPricelist()->getId()]['pricelist'] = [
|
||||
'pricelistId' => $value->getPricelist()->getId(),
|
||||
'variationId' => $value->getVariationId(),
|
||||
'price' => $value->getPrice(),
|
||||
'discount' => $value->getDiscount(),
|
||||
];
|
||||
} else { // variations
|
||||
if ($value->getPricelist()) {
|
||||
$pricelistId = $value->getPricelist()->getId();
|
||||
} else {
|
||||
$pricelistId = 0;
|
||||
}
|
||||
$return[$pricelistId][$value->getVariationId()] = [
|
||||
'pricelistId' => $pricelistId,
|
||||
'variationId' => $value->getVariationId(),
|
||||
'price' => $value->getPrice(),
|
||||
'discount' => $value->getDiscount(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function getVariations()
|
||||
{
|
||||
if (findModule('products_variations')) {
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('id, title, price')
|
||||
->from('products_variations')
|
||||
->where('id_product = ?')->setParameter(0, $this->getProductId())
|
||||
->execute();
|
||||
|
||||
if ($qb->rowCount() != 0) {
|
||||
$data = [];
|
||||
foreach ($qb as $var) {
|
||||
$data[$var['id']]['id'] = $var['id'];
|
||||
$data[$var['id']]['title'] = $var['title'];
|
||||
$data[$var['id']]['price'] = $var['price'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getVat()
|
||||
{
|
||||
if ($this->vat == null) {
|
||||
$this->vat = sqlQueryBuilder()
|
||||
->select('v.vat')
|
||||
->from('vats', 'v')
|
||||
->leftJoin('v', 'products', 'p', 'v.id=p.vat')
|
||||
->where('p.id = ?')->setParameter(0, $this->getProductId())
|
||||
->execute()->fetchColumn();
|
||||
}
|
||||
|
||||
return $this->vat;
|
||||
}
|
||||
|
||||
public function getPricelists()
|
||||
{
|
||||
$repository = $this->em->getRepository(\KupShop\PricelistBundle\Entity\Pricelist::class);
|
||||
|
||||
return $repository->findAll();
|
||||
}
|
||||
|
||||
public function getProductId()
|
||||
{
|
||||
if ($this->productId == null) {
|
||||
$this->productId = getVal('ID');
|
||||
}
|
||||
|
||||
return $this->productId;
|
||||
}
|
||||
|
||||
protected function getObject()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
(new ProductsPricelists())->run();
|
||||
@@ -0,0 +1,114 @@
|
||||
{extends file="[shared]/window.tpl"}
|
||||
|
||||
{block tabs}
|
||||
{if $body.acn == 'add'}
|
||||
{$tabTitle = {'add'|translate}}
|
||||
{else}
|
||||
{$tabTitle = {'edit'|translate}}
|
||||
{/if}
|
||||
|
||||
{windowTab id="edit" label=$tabTitle}
|
||||
{/block}
|
||||
|
||||
{block tabsContent}
|
||||
<div id="edit" class="tab-pane fade active in boxStatic box">
|
||||
{if $body.acn == 'add'}
|
||||
<h1 class="h4 main-panel-title">{'add'|translate}</h1>
|
||||
{else}
|
||||
<h1 class="h4 main-panel-title">{'edit'|translate}</h1>
|
||||
{/if}
|
||||
<div class="form-group boxFlex box-row">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>{'pricelistName'|translate}</label>
|
||||
</div>
|
||||
<div class="col-md-9 box">
|
||||
<input name="data[name]" class="form-control input-sm" value="{$body.data.name}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group boxFlex box-row">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>{'currency'|translate}</label>
|
||||
</div>
|
||||
<div class="col-md-9 box">
|
||||
<select name="data[currency]" class="selecter">
|
||||
{foreach $body.currencies as $currency}
|
||||
<option value="{$currency->getId()}"
|
||||
{if $currency->getId() == $body.data.currency}selected{/if}>{$currency->getId()} {$currency->getName()}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ifmodule PRICELISTS__ACTIVATION_TOKEN}
|
||||
<div class="form-group boxFlex box-row">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>{'token'|translate}</label>
|
||||
<a class="help-tip" data-toggle="tooltip" title="{'tokenToolTip'|translate}"><i class="bi bi-question-circle"></i></a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" name="data[token]" readonly value="{$body.data.token}">
|
||||
<div class="input-group-btn">
|
||||
{$title = 'generateToken'|translate}
|
||||
{if !empty($body.data.token)}
|
||||
{$title = 'regenerateToken'|translate}
|
||||
{/if}
|
||||
<button name="acn" value="generatePricelistToken" title="{$title}"
|
||||
class="btn btn-sm btn-success {if !empty($body.data.token)}confirm{/if}"
|
||||
{if $body.acn == 'add'}disabled{/if}>
|
||||
<span class="glyphicon glyphicon-refresh"></span> {$title}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/ifmodule}
|
||||
|
||||
{ifmodule PRICE_HISTORY}
|
||||
{if isSuperuser()}
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label ">
|
||||
<label>
|
||||
{'priceHistory'|translate}
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
{print_toggle name='price_history'}
|
||||
</div>
|
||||
</div>
|
||||
{else}
|
||||
<input type="hidden" name="data[price_history]" value="{$body.data.price_history}">
|
||||
{/if}
|
||||
{/ifmodule}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label ">
|
||||
<label>
|
||||
{'useProductDiscount'|translate}
|
||||
<span class="glyphico"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
{print_toggle name='use_product_discount' numeric=1}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label ">
|
||||
<label>
|
||||
{'coefficient'|translate}
|
||||
<span class="glyphico"></span>
|
||||
</label>
|
||||
<a data-original-title="{'coefficientToolTip'|translate}" class="help-tip" data-toggle="tooltip">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<input type="number" name="data[coefficient]" class="form-control input-sm" value="{$body.data.coefficient}" maxlength="3"
|
||||
min="0.01" max="10" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
@@ -0,0 +1,650 @@
|
||||
{extends file="[shared]/windowFrame.tpl"}
|
||||
|
||||
{block body_class}class="panel_frame"{/block}
|
||||
|
||||
{block content}
|
||||
<form id="pricelistsForm" name="editform" method="post" action="launch.php?s=products.pricelists.php&ID={$body.ID}" class="form-horizontal">
|
||||
<div id="pricelists" class="col-xs-12">
|
||||
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-7" data-form-mass-open>
|
||||
{if $body.variations}
|
||||
<a class="btn btn-sm">
|
||||
<i class="glyphicon glyphicon-plus-sign"></i>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-md-4 control-label">
|
||||
<label>{'showPricesDPH'|translate:'products'}</label>
|
||||
</div>
|
||||
<div class="col-md-1" style="">
|
||||
<div class="input-group">
|
||||
{if !$body.data.showPricesWithVat}{$vatValue = 'N'}{/if}
|
||||
{print_toggle name="showPricesWithVat" value=$vatValue}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-group panel-group-lists">
|
||||
|
||||
<div class="panel panel-heading" style="padding-bottom:4px">
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small><strong>{'price'|translate:'products'}</strong></small>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<small><strong>{'discountPricelists'|translate:'products'}</strong></small>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<small><strong>{'finalPrice'|translate:'products'}</strong></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{* Default pricelist *}
|
||||
<div class="panel-group panel-group-lists panel" data-form-item="" data-price-currency="{$body.defaultCurrency.code}" data-price-currency-rate="{$body.defaultCurrency.rate}"
|
||||
id="defaultPricelist">
|
||||
<div class="panel-heading collapsed row-green" data-toggle="collapse" data-target="#collapse_defaultPricelist">
|
||||
<div class="row" data-price="row">
|
||||
<div class="col-md-3">
|
||||
<p class="input-height"><strong>Základní ceník</strong></p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Cena</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm novat" data-price="price" name="data[0][price]" id="defaultPricelistPrice" maxlength="20"
|
||||
value="{$body.defaultPricelist.price}" type="text">
|
||||
<input class="form-control input-sm novat" data-price="priceHidden" maxlength="20" value="{$body.defaultPricelist.price}" type="hidden">
|
||||
<span class="input-group-addon">{$body.defaultCurrency.symbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Sleva</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm" data-price="discount" name="data[0][discount]" maxlength="20" value="{$body.defaultPricelist.discount}" type="text">
|
||||
<span class="input-group-addon">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" data-price="finalPrice" name="data[0][finalPrice]" class="form-control input-sm novat">
|
||||
<span class="input-group-addon">{$body.defaultCurrency.symbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $body.variations}
|
||||
<div class="col-md-1">
|
||||
<a class="btn-sm btn btn-gray" data-target="#collapse_defaultPricelist" data-toggle="collapse">
|
||||
{'variations'|translate:'products'}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{* Default pricelist Variations *}
|
||||
{if $body.variations}
|
||||
<div id="collapse_defaultPricelist" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
{foreach $body.variations as $var}
|
||||
<div class="row form-group form-group-flex no-margin" data-price="rowVar">
|
||||
<div class="col-md-3">
|
||||
<p class="input-height">{$var.title}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Cena</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm novat" data-price="priceVar" name="data[0][priceVar][{$var.id}]" maxlength="20"
|
||||
value="{if $var.price != 0}{$var.price}{/if}" type="text">
|
||||
<input class="form-control input-sm novat" data-price="priceVarHidden" data-var-id="{$var.id}" maxlength="20"
|
||||
value="{if $var.price != 0}{$var.price}{/if}" type="hidden">
|
||||
<span class="input-group-addon">{$body.defaultCurrency.symbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3"></div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm novat" data-price="finalPriceVar" name="data[0][finalPriceVar]" disabled maxlength="20" value=""
|
||||
type="text">
|
||||
<span class="input-group-addon">{$body.defaultCurrency.symbol}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="data[0][vars]" value="true">
|
||||
</div>
|
||||
|
||||
{if !$var@last}<hr style="margin-top: 5px; margin-bottom: 5px;" class="row">{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{* Pricelists saved in database *}
|
||||
{foreach $body.pricelists as $pricelist}
|
||||
<div class="panel no-margin" data-form-item="{$pricelist@index}" data-price-currency="{$pricelist->getCurrency()->getId()}"
|
||||
data-price-currency-rate="{$pricelist->getCurrency()->getRate()}">
|
||||
<div class="panel-heading collapsed row-green" data-toggle="collapse" data-target="#collapse_{$pricelist@index}">
|
||||
<div class="row" data-price="row">
|
||||
<div class="col-md-3">
|
||||
<p class="input-height"><strong>{$pricelist->getName()}</strong></p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Cena</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm novat" data-price="price" name="data[{$pricelist->getId()}][price]" id="price" maxlength="20"
|
||||
value="{$body.data.{$pricelist->getId()}.pricelist.price}" type="text">
|
||||
<input class="form-control input-sm novat" data-price="priceHidden" id="price" maxlength="20"
|
||||
value="{$body.data.{$pricelist->getId()}.pricelist.price}" type="hidden">
|
||||
<input class="form-control input-sm novat" data-price="coefficient" id="coefficient" maxlength="20"
|
||||
value="{$pricelist->getCoefficient()}" type="hidden">
|
||||
<span class="input-group-addon">{$pricelist->getCurrency()->getSymbol()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Sleva</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm" data-price="discount" name="data[{$pricelist->getId()}][discount]" maxlength="20"
|
||||
value="{if $body.data.{$pricelist->getId()}.pricelist.discount}{$body.data.{$pricelist->getId()}.pricelist.discount->printFloatValue(-8)}{/if}" type="text">
|
||||
<span class="input-group-addon">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" data-price="finalPrice" name="data[{$pricelist->getId()}][finalPrice]" class="form-control input-sm novat">
|
||||
<span class="input-group-addon">{$pricelist->getCurrency()->getSymbol()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $body.variations}
|
||||
<div class="col-md-1">
|
||||
<a class="btn-sm btn btn-gray" data-target="#collapse_{$pricelist@index}" data-toggle="collapse">
|
||||
{'variations'|translate:'products'}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{if $body.variations}
|
||||
<div id="collapse_{$pricelist@index}" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
{foreach $body.variations as $var}
|
||||
<div class="row form-group form-group-flex no-margin" data-price="rowVar">
|
||||
|
||||
<div class="col-md-3">
|
||||
<p class="input-height">{$var.title}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Cena</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
{if !$body.data.{$pricelist->getId()}.{$var.id}.price}
|
||||
{$price = $var.price}
|
||||
{if $price == 0}
|
||||
{$price = ''}
|
||||
{/if}
|
||||
{else}
|
||||
{$price = {$body.data.{$pricelist->getId()}.{$var.id}.price}}
|
||||
{/if}
|
||||
<input class="form-control input-sm novat" data-price="priceVar" data-var-id="{$var.id}"
|
||||
name="data[{$pricelist->getId()}][priceVar][{$var.id}]" maxlength="20" value="{$body.data.{$pricelist->getId()}.{$var.id}.price}"
|
||||
type="text">
|
||||
<input class="form-control input-sm novat" data-price="priceVarHidden" data-var-id="{$var.id}" maxlength="20" value="{$price}" type="hidden">
|
||||
<span class="input-group-addon">{$pricelist->getCurrency()->getSymbol()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Sleva</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm" data-price="discountVar" name="data[{$pricelist->getId()}][discountVar][{$var.id}]" maxlength="20"
|
||||
value="{$body.data.{$pricelist->getId()}.{$var.id}.discount}" type="text">
|
||||
<span class="input-group-addon">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input class="form-control input-sm novat" data-price="finalPriceVar" name="data[{$pricelist->getId()}][finalPriceVar]" disabled
|
||||
maxlength="20" value="" type="text">
|
||||
<span class="input-group-addon">{$pricelist->getCurrency()->getSymbol()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="data[{$pricelist->getId()}][vars]" value="true">
|
||||
|
||||
</div>
|
||||
{if !$var@last}<hr style="margin-top: 5px; margin-bottom: 5px;" class="row">{/if}
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-4 pull-right">
|
||||
<button class="btn btn-primary btn-block" type="submit" name="acn" value="updatePricelists">{'savePricelists'|translate:'products'}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
var Price = {
|
||||
recalcVats: function (vat, type) {
|
||||
$('[data-price=price], [data-price=priceHidden]').each(function () {
|
||||
var formGroup = Price.recalcVat($(this), vat, type);
|
||||
if (formGroup) {
|
||||
Price.setFinalPrice(formGroup.find('[data-price=finalPrice]'));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('[data-price=priceVar]').each(function () {
|
||||
var formGroup = Price.recalcVat($(this), vat, type);
|
||||
if (formGroup) {
|
||||
Price.setFinalPriceVar(formGroup.find('[data-price=finalPriceVar]'));
|
||||
}
|
||||
});
|
||||
|
||||
$('[data-price=finalPriceVar], [data-price=priceVarHidden]').each(function () {
|
||||
if (!$(this).attr('data-vat')) {
|
||||
Price.recalcVat($(this), vat, type);
|
||||
}
|
||||
});
|
||||
|
||||
$('[data-price=finalPrice]').each(function () {
|
||||
Price.recalcVat($(this), vat, type);
|
||||
});
|
||||
},
|
||||
|
||||
recalcVat: function (element, vat, type) {
|
||||
var priceVat;
|
||||
var $price = element,
|
||||
$value;
|
||||
|
||||
if ($price.val()) {
|
||||
$value = new BigNumber($price.val());
|
||||
|
||||
switch (type) {
|
||||
// calculate price with vat
|
||||
case 1:
|
||||
var tmpPriceWithVat = $value.times((100 + vat) / 100).round(4).toNumber();
|
||||
priceVat = Math.round((tmpPriceWithVat + Number.EPSILON) * 100) / 100;
|
||||
break;
|
||||
// price without vat
|
||||
case 0:
|
||||
priceVat = $value.times(100).dividedBy(100 + vat).round(4);//$price.val() * 100 / (100 + vat)));
|
||||
break;
|
||||
}
|
||||
$price.val(priceVat);
|
||||
|
||||
return element.parents('.form-group');
|
||||
}
|
||||
},
|
||||
|
||||
recalcFinalPrice: function (price, discount) {
|
||||
var discount_price;
|
||||
if (discount == '' || discount == undefined) {
|
||||
discount = 0;
|
||||
}
|
||||
|
||||
if (price == undefined) {
|
||||
price = 0;
|
||||
}
|
||||
|
||||
discount = new BigNumber(discount);
|
||||
discount_price = new BigNumber(100).minus(discount).dividedBy(new BigNumber(100)).times(price).round(4); //formatPrice(price * ((100 - discount) / 100));
|
||||
|
||||
return discount_price;
|
||||
},
|
||||
|
||||
recalcDiscount: function (price, finalPrice) {
|
||||
price = new BigNumber(price);
|
||||
finalPrice = new BigNumber(finalPrice);
|
||||
|
||||
var discount;
|
||||
if (finalPrice.greaterThan(new BigNumber(0))) {
|
||||
discount = new BigNumber(100).minus(finalPrice.dividedBy(price).times(100)).round(4);//formatPrice(100 - (discount_price / price) * 100));
|
||||
}
|
||||
|
||||
return discount;
|
||||
},
|
||||
|
||||
setPrice: function (element, price) {
|
||||
this.getHiddenPrice(element).val(price);
|
||||
this.getPrice(element).val(price);
|
||||
},
|
||||
|
||||
setHiddenPrice: function (element, price) {
|
||||
this.getHiddenPrice(element).val(price);
|
||||
},
|
||||
|
||||
setHiddenPriceVar: function (element, price) {
|
||||
this.getHiddenPriceVar(element).val(price);
|
||||
},
|
||||
|
||||
setPriceVar: function (element, price) {
|
||||
this.getPriceVar(element).val(price);
|
||||
},
|
||||
|
||||
setDiscount: function (element) {
|
||||
var hiddenPrice;
|
||||
if (this.getHiddenPrice(element).val() == '') {
|
||||
hiddenPrice = 0;
|
||||
} else {
|
||||
hiddenPrice = this.getHiddenPrice(element).val();
|
||||
}
|
||||
|
||||
|
||||
var discount = this.recalcDiscount(
|
||||
hiddenPrice,
|
||||
this.getFinalPrice(element).val()
|
||||
);
|
||||
this.getDiscount(element).val(discount);
|
||||
},
|
||||
|
||||
setFinalPrice: function (element) {
|
||||
var price;
|
||||
if (this.getPrice(element).val() != '') {
|
||||
price = this.getPrice(element).val();
|
||||
} else {
|
||||
price = this.getHiddenPrice(element).val();
|
||||
}
|
||||
|
||||
|
||||
var finalPrice = this.recalcFinalPrice(
|
||||
price,
|
||||
this.getDiscount(element).val()
|
||||
);
|
||||
|
||||
if (this.getDiscount(element).val() === '') {
|
||||
this.getFinalPrice(element).val(price);
|
||||
} else {
|
||||
this.getFinalPrice(element).val(finalPrice);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
setFinalPriceVar: function (element) {
|
||||
var finalPrice;
|
||||
if (this.getPriceVar(element).val() !== '') {
|
||||
finalPrice = this.recalcFinalPrice(
|
||||
this.getPriceVar(element).val(),
|
||||
this.getDiscountVar(element).val()
|
||||
);
|
||||
this.getFinalPriceVar(element).val(finalPrice);
|
||||
this.getFinalPriceVar(element).attr('data-vat', 1);
|
||||
} else {
|
||||
var row = this.getRow(element);
|
||||
var priceVar = this.getHiddenPriceVar(element).val();
|
||||
if (priceVar != '') {
|
||||
finalPrice = this.recalcFinalPrice(
|
||||
priceVar,
|
||||
this.getDiscount(row).val()
|
||||
);
|
||||
} else {
|
||||
finalPrice = this.recalcFinalPrice(
|
||||
this.getHiddenPrice(row).val(),
|
||||
this.getDiscount(row).val()
|
||||
);
|
||||
}
|
||||
this.getFinalPriceVar(element).val(finalPrice);
|
||||
}
|
||||
},
|
||||
|
||||
checkEmptyPricelistPrice: function (element) {
|
||||
var pricelistPrice = this.getPrice(element).val();
|
||||
var pricelist = element.parents('.panel');
|
||||
var currency = pricelist.data('price-currency'),
|
||||
rate = pricelist.data('price-currency-rate');
|
||||
let coefficient = this.getRow(element).find('[data-price=coefficient]').val();
|
||||
|
||||
|
||||
if (pricelistPrice === '') {
|
||||
pricelistPrice = this.getPrice(this.getDefaultRow()).val();
|
||||
if (coefficient) {
|
||||
pricelistPrice = pricelistPrice * coefficient
|
||||
}
|
||||
var price = this.recalcCurrency(currency, pricelistPrice, rate);
|
||||
//this.setPrice(element, price);
|
||||
this.setHiddenPrice(element, price);
|
||||
}
|
||||
},
|
||||
|
||||
checkEmptyVariations: function (element) {
|
||||
var idVar = element.data('var-id'),
|
||||
priceVar = element.val();
|
||||
|
||||
var finalPrice, currency, rate, pricelist;
|
||||
var discount;
|
||||
let coefficient = this.getRow(element).find('[data-price=coefficient]').val();
|
||||
// not default pricelist
|
||||
if (idVar) {
|
||||
var defaultVarPrice = $('input[name="data[0][priceVar][' + idVar + ']"]').val();
|
||||
// have variation an default variation price in default pricelist
|
||||
if (defaultVarPrice && priceVar == '') {
|
||||
//apply pricelist coefficient
|
||||
if (coefficient) {
|
||||
defaultVarPrice = defaultVarPrice * coefficient
|
||||
}
|
||||
// currency data
|
||||
pricelist = element.parents('.panel');
|
||||
currency = pricelist.data('price-currency');
|
||||
rate = pricelist.data('price-currency-rate');
|
||||
|
||||
discount = this.getDiscount(element).val();
|
||||
finalPrice = this.recalcFinalPrice(defaultVarPrice, discount);
|
||||
finalPrice = this.recalcCurrency(currency, finalPrice, rate);
|
||||
this.getHiddenPriceVar(element).val(this.recalcCurrency(currency, defaultVarPrice, rate));
|
||||
this.getFinalPriceVar(element).val(finalPrice);
|
||||
}
|
||||
} else { // default pricelist
|
||||
if (!element.val()) {
|
||||
var defaultPrice = this.getPrice(this.getDefaultRow()).val();
|
||||
//apply pricelist coefficient
|
||||
if (coefficient) {
|
||||
defaultPrice = defaultPrice * coefficient
|
||||
}
|
||||
discount = this.getDiscount(this.getDefaultRow()).val();
|
||||
finalPrice = this.recalcFinalPrice(defaultPrice, discount);
|
||||
this.getFinalPriceVar(element).val(finalPrice);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
checkFinalPriceVar: function (element) {
|
||||
var price = this.getPriceVar(element);
|
||||
var varId = price.data('var-id');
|
||||
if (price.val() == '' && varId) {
|
||||
var defaultVarPrice = $('input[name="data[0][priceVar][' + varId + ']"]').val();
|
||||
if (defaultVarPrice == '') {
|
||||
var pricelistPrice = this.getHiddenPrice(element).val();
|
||||
this.setHiddenPriceVar(element, pricelistPrice);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
recalcCurrency: function (currency, price, currencyRate) {
|
||||
var defaultCurrency = this.getDefaultRow().data('price-currency');
|
||||
if (currency != defaultCurrency) {
|
||||
price = new BigNumber(price);
|
||||
price = price.dividedBy(currencyRate);
|
||||
}
|
||||
|
||||
return price;
|
||||
},
|
||||
|
||||
getVarRow: function (element) {
|
||||
return $(element).closest('[data-price=rowVar]');
|
||||
},
|
||||
|
||||
getRow: function (element) {
|
||||
return $(element).closest('.panel').find('[data-price=row]');
|
||||
},
|
||||
|
||||
getDefaultRow: function () {
|
||||
return $('#defaultPricelist');
|
||||
},
|
||||
|
||||
getPrice: function (element) {
|
||||
return this.getRow(element).find('[data-price=price]');
|
||||
},
|
||||
|
||||
getHiddenPrice: function (element) {
|
||||
return this.getRow(element).find('[data-price=priceHidden]');
|
||||
},
|
||||
|
||||
getHiddenPriceVar: function (element) {
|
||||
return this.getVarRow(element).find('[data-price=priceVarHidden]');
|
||||
},
|
||||
|
||||
getPriceVar: function (element) {
|
||||
return this.getVarRow(element).find('[data-price=priceVar]');
|
||||
},
|
||||
|
||||
getDiscount: function (element) {
|
||||
return this.getRow(element).find('[data-price=discount]');
|
||||
},
|
||||
|
||||
getDiscountVar: function (element) {
|
||||
var discountVar = this.getVarRow(element).find('[data-price=discountVar]');
|
||||
if (!discountVar.val()) {
|
||||
discountVar = this.getDiscount(element);
|
||||
}
|
||||
|
||||
return discountVar;
|
||||
},
|
||||
|
||||
getFinalPrice: function (element) {
|
||||
return this.getRow(element).find('[data-price=finalPrice]');
|
||||
},
|
||||
|
||||
getFinalPriceVar: function (element) {
|
||||
return this.getVarRow(element).find('[data-price=finalPriceVar]');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// keyups
|
||||
$('[data-price="discount"]').keyup(function (e) {
|
||||
if (e.originalEvent !== undefined) {
|
||||
Price.setFinalPrice($(this));
|
||||
}
|
||||
{if $body.variations}
|
||||
// update variations final prices
|
||||
$('[data-price="priceVar"]').each(function () {
|
||||
Price.setFinalPriceVar($(this));
|
||||
});
|
||||
{/if}
|
||||
});
|
||||
$('[data-price="discountVar"]').keyup(function (e) {
|
||||
if (e.originalEvent !== undefined) {
|
||||
Price.setFinalPriceVar($(this));
|
||||
}
|
||||
});
|
||||
$('[data-price="price"]').keyup(function () {
|
||||
Price.setDiscount($(this));
|
||||
if ($(this).val() != '') {
|
||||
Price.setHiddenPrice($(this), $(this).val());
|
||||
}
|
||||
$('[data-price="price"]').each(function () {
|
||||
Price.checkEmptyPricelistPrice($(this));
|
||||
$('[data-price="discount"]').trigger('keyup');
|
||||
});
|
||||
$('[data-price="discount"]').each(function () {
|
||||
Price.setFinalPrice($(this));
|
||||
});
|
||||
});
|
||||
$('[data-price="finalPrice"]').keyup(function () {
|
||||
Price.setDiscount($(this));
|
||||
$('[data-price="discount"]').trigger('keyup');
|
||||
});
|
||||
{if $body.variations}
|
||||
$('[data-price="priceVar"]').keyup(function () {
|
||||
// When is variation price deleted check finalPrices
|
||||
if (!$(this).val()) {
|
||||
$('[data-price="finalPriceVar"]').each(function () {
|
||||
Price.checkFinalPriceVar($(this));
|
||||
$('[data-price="discount"]').trigger('keyup');
|
||||
});
|
||||
}
|
||||
Price.setHiddenPriceVar($(this), $(this).val());
|
||||
Price.setFinalPriceVar($(this));
|
||||
$('[data-price="priceVar"]').each(function () {
|
||||
Price.checkEmptyVariations($(this));
|
||||
});
|
||||
});
|
||||
{/if}
|
||||
|
||||
// onpage load
|
||||
$(document).ready(function () {
|
||||
$('[data-price="price"]').each(function () {
|
||||
Price.checkEmptyPricelistPrice($(this));
|
||||
});
|
||||
{if $body.variations}
|
||||
$('[data-price="priceVar"]').each(function () {
|
||||
Price.setFinalPriceVar($(this));
|
||||
Price.checkEmptyVariations($(this));
|
||||
});
|
||||
{/if}
|
||||
$('[data-price="discount"]').each(function () {
|
||||
Price.setFinalPrice($(this));
|
||||
});
|
||||
if ($('input[name="data[showPricesWithVat]"]').is(':checked')) {
|
||||
Price.recalcVats({$body.vat}, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// vat switch
|
||||
$('input[name="data[showPricesWithVat]"]').change(function () {
|
||||
if ($(this).is(':checked')) {
|
||||
Price.recalcVats({$body.vat}, 1);
|
||||
} else {
|
||||
Price.recalcVats({$body.vat}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
{block initAddForm}
|
||||
initForm({
|
||||
selector: '#pricelists',
|
||||
beforeAdd: function (original) {
|
||||
var $item = original();
|
||||
}
|
||||
});
|
||||
{/block}
|
||||
|
||||
</script>
|
||||
{/block}
|
||||
|
||||
148
bundles/KupShop/PricelistBundle/Context/PricelistContext.php
Normal file
148
bundles/KupShop/PricelistBundle/Context/PricelistContext.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Context;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use KupShop\KupShopBundle\Context\ContextInterface;
|
||||
use KupShop\KupShopBundle\Context\EmptiableContext;
|
||||
use KupShop\KupShopBundle\Context\UserContext;
|
||||
use KupShop\PricelistBundle\Entity\Pricelist;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class PricelistContext implements ContextInterface, EmptiableContext
|
||||
{
|
||||
public const PERSISTENT_KEY = 'persistentPricelist';
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
private UserContext $userContext;
|
||||
|
||||
protected ?string $activeId = null;
|
||||
private bool $loaded = false;
|
||||
|
||||
/** @var array<string, Pricelist> */
|
||||
protected array $activeCache = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function activate(?string $id)
|
||||
{
|
||||
if (null === $id) {
|
||||
trigger_error('Passing $id=null into ContextInterface::activate is deprecated! Use ::clearCache() instead.', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->activeId = $id;
|
||||
$this->loaded = true;
|
||||
} else {
|
||||
$this->clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
public function getActive(): ?Pricelist
|
||||
{
|
||||
if (null !== ($id = $this->getActiveId())) {
|
||||
if (array_key_exists($id, $this->activeCache)) {
|
||||
return $this->activeCache[$id];
|
||||
} else {
|
||||
return $this->activeCache[$id] = $this->loadPricelistById($id);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function loadActive()
|
||||
{
|
||||
if ($user = $this->userContext->getActive()) {
|
||||
if (!empty($user->id_pricelist)) {
|
||||
return $user->id_pricelist;
|
||||
}
|
||||
|
||||
foreach ($user->getGroups() as $group) {
|
||||
if (!empty($group['id_pricelist'])) {
|
||||
return $group['id_pricelist'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getActiveId(): ?string
|
||||
{
|
||||
return $this->isEmpty() ? null : $this->activeId ??= $this->loadActive();
|
||||
}
|
||||
|
||||
protected function loadPricelistById($id)
|
||||
{
|
||||
return $this->em->getRepository(Pricelist::class)->find($id);
|
||||
}
|
||||
|
||||
public function getDefault(): ?Pricelist
|
||||
{
|
||||
if (!$this->getDefaultId()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->em->getRepository(Pricelist::class)->find($this->getDefaultId());
|
||||
}
|
||||
|
||||
public function getDefaultId(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function loadPricelistByCurrency($currency)
|
||||
{
|
||||
return $this->em->getRepository(Pricelist::class)->findOneBy(['currency' => $currency]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Pricelist[]
|
||||
*/
|
||||
public function getSupported(): array
|
||||
{
|
||||
return $this->em->getRepository(Pricelist::class)->findAll();
|
||||
}
|
||||
|
||||
public function clearCache(): void
|
||||
{
|
||||
$this->loaded = false;
|
||||
$this->activeId = null;
|
||||
}
|
||||
|
||||
public function forceEmpty(): void
|
||||
{
|
||||
$this->loaded = true;
|
||||
$this->activeId = null;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->loaded && $this->activeId === null;
|
||||
}
|
||||
|
||||
public function isValid(string $id): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMultipleActiveIds(int $priceListId): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function _setUserContext(UserContext $userContext): void
|
||||
{
|
||||
$this->userContext = $userContext;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function _setEm(EntityManagerInterface $em): void
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
}
|
||||
193
bundles/KupShop/PricelistBundle/Entity/Pricelist.php
Normal file
193
bundles/KupShop/PricelistBundle/Entity/Pricelist.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class Pricelist.
|
||||
*
|
||||
* @ORM\Entity
|
||||
*
|
||||
* @ORM\Table(name="pricelists", options={"collate":"utf8"})
|
||||
*/
|
||||
class Pricelist
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=60)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="boolean", name="use_product_discount")
|
||||
*/
|
||||
private $useProductDiscount;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="float", name="coefficient")
|
||||
*/
|
||||
private $coefficient;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\I18nBundle\Entity\Currency")
|
||||
*
|
||||
* @ORM\JoinColumn(name="currency")
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="KupShop\PricelistBundle\Entity\PricelistProduct", mappedBy="pricelist", cascade={"remove", "persist"})
|
||||
*/
|
||||
private $product;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->product = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set currency.
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setCurrency(?\KupShop\I18nBundle\Entity\Currency $currency = null)
|
||||
{
|
||||
$this->currency = $currency;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currency.
|
||||
*
|
||||
* @return \KupShop\I18nBundle\Entity\Currency
|
||||
*/
|
||||
public function getCurrency()
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add product.
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function addProduct(PricelistProduct $product)
|
||||
{
|
||||
$this->product[] = $product;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove product.
|
||||
*/
|
||||
public function removeProduct(PricelistProduct $product)
|
||||
{
|
||||
$this->product->removeElement($product);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product.
|
||||
*
|
||||
* @return \Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function getProduct()
|
||||
{
|
||||
return $this->product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extend product discount field.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseProductDiscount()
|
||||
{
|
||||
return $this->useProductDiscount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set use product discount field.
|
||||
*
|
||||
* @param bool $useProductDiscount
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setUseProductDiscount($useProductDiscount)
|
||||
{
|
||||
$this->useProductDiscount = $useProductDiscount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coefficient field.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getCoefficient()
|
||||
{
|
||||
return $this->coefficient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set use product discount field.
|
||||
*
|
||||
* @param float $coefficient
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setCoefficient($coefficient)
|
||||
{
|
||||
$this->coefficient = $coefficient;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
90
bundles/KupShop/PricelistBundle/Entity/Pricelist.php~
Normal file
90
bundles/KupShop/PricelistBundle/Entity/Pricelist.php~
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class Pricelist.
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="pricelists", options={"collate":"utf8"})
|
||||
*/
|
||||
class Pricelist
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=60)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\I18nBundle\Entity\Currency")
|
||||
* @ORM\JoinColumn(name="currency")
|
||||
*/
|
||||
private $currency;
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set currency.
|
||||
*
|
||||
* @param \KupShop\I18nBundle\Entity\Currency $currency
|
||||
*
|
||||
* @return Pricelist
|
||||
*/
|
||||
public function setCurrency(\KupShop\I18nBundle\Entity\Currency $currency = null)
|
||||
{
|
||||
$this->currency = $currency;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currency.
|
||||
*
|
||||
* @return \KupShop\I18nBundle\Entity\Currency
|
||||
*/
|
||||
public function getCurrency()
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
}
|
||||
186
bundles/KupShop/PricelistBundle/Entity/PricelistProduct.php
Normal file
186
bundles/KupShop/PricelistBundle/Entity/PricelistProduct.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\ORM\Mapping\UniqueConstraint;
|
||||
|
||||
/**
|
||||
* Class ProductPricelist.
|
||||
*
|
||||
* @ORM\Entity
|
||||
*
|
||||
* @ORM\Table(name="pricelists_products",
|
||||
* uniqueConstraints={@UniqueConstraint(name="pricelist_product",
|
||||
* columns={"id_pricelist","id_product","id_variation"})})
|
||||
*/
|
||||
class PricelistProduct
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
*
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\PricelistBundle\Entity\Pricelist", inversedBy="product")
|
||||
*
|
||||
* @ORM\JoinColumn(name="id_pricelist", referencedColumnName="id", onDelete="CASCADE", nullable=false)
|
||||
*/
|
||||
private $pricelist;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne()
|
||||
*
|
||||
* @ORM\Column(name="id_product", type="integer")
|
||||
*/
|
||||
private $productId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_variation", type="integer", nullable=true)
|
||||
*
|
||||
* @ORM\ManyToOne(cascade={"persist", "remove"})
|
||||
*/
|
||||
private $variationId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="decimal", precision=15, scale=4, nullable=true)
|
||||
*/
|
||||
private $price;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="decimal", scale=8, precision=12, nullable=true)
|
||||
*/
|
||||
private $discount;
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set productId.
|
||||
*
|
||||
* @param int $productId
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setProductId($productId)
|
||||
{
|
||||
$this->productId = $productId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get productId.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getProductId()
|
||||
{
|
||||
return $this->productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set variationId.
|
||||
*
|
||||
* @param int $variationId
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setVariationId($variationId)
|
||||
{
|
||||
$this->variationId = $variationId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variationId.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getVariationId()
|
||||
{
|
||||
return $this->variationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set price.
|
||||
*
|
||||
* @param string $price
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setPrice($price)
|
||||
{
|
||||
$this->price = $price;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get price.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set discount.
|
||||
*
|
||||
* @param string $discount
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setDiscount($discount)
|
||||
{
|
||||
$this->discount = $discount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get discount.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscount()
|
||||
{
|
||||
return $this->discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pricelistId.
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setPricelist(?Pricelist $pricelist = null)
|
||||
{
|
||||
$this->pricelist = $pricelist;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pricelistId.
|
||||
*
|
||||
* @return \KupShop\PricelistBundle\Entity\Pricelist
|
||||
*/
|
||||
public function getPricelist()
|
||||
{
|
||||
return $this->pricelist;
|
||||
}
|
||||
}
|
||||
156
bundles/KupShop/PricelistBundle/Entity/PricelistProduct.php~
Normal file
156
bundles/KupShop/PricelistBundle/Entity/PricelistProduct.php~
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class ProductPricelist.
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="pricelists_products")
|
||||
*/
|
||||
class PricelistProduct
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\PricelistBundle\Entity\Pricelist")
|
||||
* @ORM\JoinColumn(name="id_pricelist")
|
||||
*/
|
||||
private $pricelistId;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne()
|
||||
* @ORM\Column(name="id_product", type="integer")
|
||||
*/
|
||||
private $productId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_variation", type="integer", nullable=true)
|
||||
* @ORM\ManyToOne()
|
||||
*/
|
||||
private $variationId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="decimal", precision=15, scale=4, nullable=true)
|
||||
*/
|
||||
private $price;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
private $discount;
|
||||
|
||||
/**
|
||||
* Get id.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set price.
|
||||
*
|
||||
* @param string $price
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setPrice($price)
|
||||
{
|
||||
$this->price = $price;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get price.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set discount.
|
||||
*
|
||||
* @param int $discount
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setDiscount($discount)
|
||||
{
|
||||
$this->discount = $discount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get discount.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDiscount()
|
||||
{
|
||||
return $this->discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pricelistId.
|
||||
*
|
||||
* @param \KupShop\PricelistBundle\Entity\Pricelist $pricelistId
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setPricelistId(\KupShop\PricelistBundle\Entity\Pricelist $pricelistId = null)
|
||||
{
|
||||
$this->pricelistId = $pricelistId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pricelistId.
|
||||
*
|
||||
* @return \KupShop\PricelistBundle\Entity\Pricelist
|
||||
*/
|
||||
public function getPricelistId()
|
||||
{
|
||||
return $this->pricelistId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set variationId.
|
||||
*
|
||||
* @param int $variationId
|
||||
*
|
||||
* @return PricelistProduct
|
||||
*/
|
||||
public function setVariationId($variationId)
|
||||
{
|
||||
$this->variationId = $variationId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variationId.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getVariationId()
|
||||
{
|
||||
return $this->variationId;
|
||||
}
|
||||
}
|
||||
159
bundles/KupShop/PricelistBundle/Entity/ProductPricelist.php~
Normal file
159
bundles/KupShop/PricelistBundle/Entity/ProductPricelist.php~
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class ProductPricelist
|
||||
* @package KupShop\PricelistBundle\Entity
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="pricelists_products")
|
||||
*/
|
||||
class ProductPricelist
|
||||
{
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\PricelistBundle\Entity\Pricelist")
|
||||
* @ORM\JoinColumn(name="id_pricelist")
|
||||
*/
|
||||
private $pricelistId;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="KupShop\CatalogBundle\Entity\Product")
|
||||
* @ORM\JoinColumn(name="id_product")
|
||||
*/
|
||||
private $productId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="id_variation", type="integer")
|
||||
*/
|
||||
private $variationId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="decimal", precision=15, scale=4)
|
||||
*/
|
||||
private $price;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $discount;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set price
|
||||
*
|
||||
* @param string $price
|
||||
*
|
||||
* @return ProductPricelist
|
||||
*/
|
||||
public function setPrice($price)
|
||||
{
|
||||
$this->price = $price;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get price
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set discount
|
||||
*
|
||||
* @param integer $discount
|
||||
*
|
||||
* @return ProductPricelist
|
||||
*/
|
||||
public function setDiscount($discount)
|
||||
{
|
||||
$this->discount = $discount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get discount
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getDiscount()
|
||||
{
|
||||
return $this->discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pricelistId
|
||||
*
|
||||
* @param \KupShop\PricelistBundle\Entity\Pricelist $pricelistId
|
||||
*
|
||||
* @return ProductPricelist
|
||||
*/
|
||||
public function setPricelistId(\KupShop\PricelistBundle\Entity\Pricelist $pricelistId = null)
|
||||
{
|
||||
$this->pricelistId = $pricelistId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pricelistId
|
||||
*
|
||||
* @return \KupShop\PricelistBundle\Entity\Pricelist
|
||||
*/
|
||||
public function getPricelistId()
|
||||
{
|
||||
return $this->pricelistId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set productId
|
||||
*
|
||||
* @param \KupShop\CatalogBundle\Entity\Product $productId
|
||||
*
|
||||
* @return ProductPricelist
|
||||
*/
|
||||
public function setProductId(\KupShop\CatalogBundle\Entity\Product $productId = null)
|
||||
{
|
||||
$this->productId = $productId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get productId
|
||||
*
|
||||
* @return \KupShop\CatalogBundle\Entity\Product
|
||||
*/
|
||||
public function getProductId()
|
||||
{
|
||||
return $this->productId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\EventListener;
|
||||
|
||||
use KupShop\KupShopBundle\Event\CreateMenuEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class CreateMenuListener implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
CreateMenuEvent::COMPLETING_TREE => [
|
||||
['addItem', 200],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var CreateMenuEvent
|
||||
*/
|
||||
public function addItem(CreateMenuEvent $event)
|
||||
{
|
||||
$event->addItem('productsMenu',
|
||||
[
|
||||
'name' => 'pricelist',
|
||||
'left' => 's=menu.php&type=pricelist', 'right' => 's=list.php&type=pricelist',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\EventListener;
|
||||
|
||||
use KupShop\KupShopBundle\Exception\RedirectException;
|
||||
use KupShop\KupShopBundle\Util\Contexts;
|
||||
use KupShop\KupShopBundle\Util\RequestUtil;
|
||||
use KupShop\PricelistBundle\Context\PricelistContext;
|
||||
use Query\Operator;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\ControllerEvent;
|
||||
|
||||
class RequestListener implements EventSubscriberInterface
|
||||
{
|
||||
/** @required */
|
||||
public RequestUtil $requestUtil;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
$listeners = [];
|
||||
|
||||
if (findModule(\Modules::PRICELISTS, \Modules::SUB_ACTIVATION_TOKEN)) {
|
||||
$listeners[] = ['handleTokenPricelist', 200];
|
||||
}
|
||||
|
||||
$listeners[] = ['handlePersistentPricelist', 200];
|
||||
|
||||
return [
|
||||
ControllerEvent::class => $listeners,
|
||||
];
|
||||
}
|
||||
|
||||
public function handleTokenPricelist(ControllerEvent $event)
|
||||
{
|
||||
$pricelistContext = Contexts::get(PricelistContext::class);
|
||||
if ($pricelistContext->getActiveId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
if ($token = $request->get('wpj_activate_pricelist', $request->get('wpjplid'))) {
|
||||
$pricelist = sqlQueryBuilder()->select('id')->from('pricelists')
|
||||
->where(Operator::equals(['token' => $token]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
if ($pricelist) {
|
||||
$session = $request->getSession();
|
||||
$session->set(PricelistContext::PERSISTENT_KEY, $pricelist);
|
||||
}
|
||||
|
||||
$this->requestUtil->modifyQueryParameters($request, function ($queryParams) {
|
||||
unset($queryParams['wpj_activate_pricelist'], $queryParams['wpjplid']);
|
||||
|
||||
return $queryParams;
|
||||
});
|
||||
|
||||
throw new RedirectException($request->getUri());
|
||||
}
|
||||
}
|
||||
|
||||
public function handlePersistentPricelist(ControllerEvent $event)
|
||||
{
|
||||
if ($pricelist = $event->getRequest()->getSession()->get(PricelistContext::PERSISTENT_KEY)) {
|
||||
Contexts::get(PricelistContext::class)->activate($pricelist);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
bundles/KupShop/PricelistBundle/PricelistBundle.php
Normal file
9
bundles/KupShop/PricelistBundle/PricelistBundle.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class PricelistBundle extends Bundle
|
||||
{
|
||||
}
|
||||
113
bundles/KupShop/PricelistBundle/Query/Product.php
Normal file
113
bundles/KupShop/PricelistBundle/Query/Product.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Query;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use KupShop\KupShopBundle\Util\Contexts;
|
||||
use KupShop\PricelistBundle\Context\PricelistContext;
|
||||
use Query\Operator;
|
||||
use Query\QueryBuilder;
|
||||
|
||||
class Product
|
||||
{
|
||||
public static function getPriceListDiscountSpec($pricelistId, $condition)
|
||||
{
|
||||
$priceListIds = static::getActivePriceLists($pricelistId);
|
||||
|
||||
return function (QueryBuilder $qb) use ($priceListIds, $condition) {
|
||||
$qb->leftJoin('p', 'pricelists_products', 'prlp', 'prlp.id_product = p.id AND prlp.id_variation IS null AND prlp.id_pricelist IN (:id_pricelist)')
|
||||
->setParameter('id_pricelist', $priceListIds, Connection::PARAM_INT_ARRAY);
|
||||
|
||||
return $condition;
|
||||
};
|
||||
}
|
||||
|
||||
public static function applyPricelist($pricelist, $variation = null, $selectMinPrice = false)
|
||||
{
|
||||
$pricelistSelect = $selectMinPrice ?
|
||||
'MIN(COALESCE(prlv.price, pv.price, prlp.price, p.price)) as pricelist_price, '
|
||||
.'MAX(COALESCE(prlv.price, pv.price, prlp.price, p.price)) as pricelist_price_max'
|
||||
|
||||
: 'COALESCE(prlv.price, pv.price, prlp.price, p.price) as pricelist_price';
|
||||
|
||||
if (findModule(\Modules::PRICE_HISTORY)) {
|
||||
$priceForDiscountSelect = $selectMinPrice ?
|
||||
'MIN(COALESCE(prlv.price_for_discount, prlp.price_for_discount)) as pricelist_price_for_discount,
|
||||
MAX(COALESCE(prlv.price_for_discount, prlp.price_for_discount)) as pricelist_price_for_discount_max'
|
||||
|
||||
: 'COALESCE(prlv.price_for_discount, prlp.price_for_discount) as pricelist_price_for_discount';
|
||||
|
||||
$pricelistSelect .= ', COALESCE(prl_v.price_history, prl.price_history) as pricelist_price_history, '.$priceForDiscountSelect;
|
||||
}
|
||||
|
||||
return function (QueryBuilder $qb) use ($variation, $pricelistSelect, $pricelist) {
|
||||
$qb->addSelect(
|
||||
$pricelistSelect,
|
||||
'COALESCE(prlv.discount, prlp.discount) as pricelist_discount',
|
||||
'COALESCE(prlv.id_pricelist, prlp.id_pricelist) as from_pricelist',
|
||||
'COALESCE(IF(prlv.price IS NULL, NULL, "prlv"),
|
||||
IF(prlp.price IS NULL, NULL, "prlp"),
|
||||
IF(pv.price IS NULL, NULL, "pv"),
|
||||
IF(p.price IS NULL, NULL, "p")) as price_source',
|
||||
'COALESCE(IF(prlv.price IS NULL, NULL, prl_v.currency),
|
||||
IF(pv.price IS NULL, NULL, ""),
|
||||
IF(prlp.price IS NULL, NULL, prl.currency),
|
||||
IF(p.price IS NULL, NULL, "")) as pricelist_currency'
|
||||
)
|
||||
->joinPriceListsProducts($pricelist);
|
||||
|
||||
if ($variation) {
|
||||
$qb->andWhere(Operator::equals(['pv.id' => $variation]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static function applyPricelistOnVariation($pricelist)
|
||||
{
|
||||
$priceListSelect = 'COALESCE(prlv.price, pv.price, prlp.price, p.price) as pricelist_price, COALESCE(prlv.discount, prlp.discount) as pricelist_discount';
|
||||
if (findModule(\Modules::PRICE_HISTORY)) {
|
||||
$priceListSelect .= ', COALESCE(prl_v.price_history, prl.price_history) as pricelist_price_history, COALESCE(prlv.price_for_discount, prlp.price_for_discount) as pricelist_price_for_discount';
|
||||
}
|
||||
|
||||
$priceListIds = static::getActivePriceLists((int) $pricelist);
|
||||
|
||||
return function (QueryBuilder $qb) use ($priceListSelect, $priceListIds) {
|
||||
$qb->addSelect($priceListSelect,
|
||||
'COALESCE(prlv.id_pricelist, prlp.id_pricelist) as from_pricelist',
|
||||
'COALESCE(IF(prlv.price IS NULL, NULL, "prlv"),
|
||||
IF(prlp.price IS NULL, NULL, "prlp"),
|
||||
IF(pv.price IS NULL, NULL, "pv"),
|
||||
IF(p.price IS NULL, NULL, "p")) as price_source',
|
||||
'COALESCE(IF(prlv.price IS NULL, NULL, prl_v.currency),
|
||||
IF(pv.price IS NULL, NULL, ""),
|
||||
IF(prlp.price IS NULL, NULL, prl.currency),
|
||||
IF(p.price IS NULL, NULL, "")) as pricelist_currency',
|
||||
'COALESCE(prlv.discount, prlp.discount) as pricelistDiscount')
|
||||
->leftJoin('pv', 'pricelists_products', 'prlp', 'prlp.id_product = pv.id_product AND prlp.id_variation IS null AND prlp.id_pricelist IN (:id_pricelist)')
|
||||
->leftJoin('pv', 'pricelists_products', 'prlv', 'prlv.id_product = pv.id_product AND prlv.id_variation = pv.id AND prlv.id_pricelist IN (:id_pricelist)')
|
||||
->leftJoin('prlp', 'pricelists', 'prl', 'prlp.id_pricelist = prl.id')
|
||||
->leftJoin('prlv', 'pricelists', 'prl_v', 'prlv.id_pricelist = prl_v.id')
|
||||
->setParameter('id_pricelist', $priceListIds, Connection::PARAM_INT_ARRAY);
|
||||
|
||||
if (count($priceListIds) <= 1) {
|
||||
$qb->setForceIndexForJoin('prlp', 'pricelist_product')
|
||||
->setForceIndexForJoin('prlv', 'pricelist_product');
|
||||
}
|
||||
|
||||
if (!$qb->isAliasPresent('p')) {
|
||||
$qb->leftJoin('pv', 'products', 'p', 'p.id = pv.id_product');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static function getActivePriceLists(int $priceListId): array
|
||||
{
|
||||
$priceListContext = Contexts::get(PricelistContext::class);
|
||||
|
||||
if ($priceListIds = $priceListContext->getMultipleActiveIds($priceListId)) {
|
||||
return $priceListIds;
|
||||
}
|
||||
|
||||
return [$priceListId];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
KupShop\PricelistBundle\:
|
||||
resource: ../../{Context,EventListener,Util}
|
||||
exclude: ../../Util/Price/PriceListPrice.php
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
class PricelistUpgrade extends UpgradeNew
|
||||
{
|
||||
protected $priority = 1100;
|
||||
|
||||
public function check_tableExists()
|
||||
{
|
||||
return $this->checkTableExists('pricelists');
|
||||
}
|
||||
|
||||
public function check_tablePricelistsProductsExists()
|
||||
{
|
||||
return $this->checkTableExists('pricelists_products');
|
||||
}
|
||||
|
||||
public function check_uniqueIdVariation()
|
||||
{
|
||||
return $this->checkIndexOnColumnExists('pricelists_products', 'id_variation');
|
||||
}
|
||||
|
||||
/** Pricelists_products set (id_pricelist, id_product, id_variation) unique */
|
||||
public function upgrade_uniqueIdVariation()
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD CONSTRAINT pricelist_product UNIQUE (id_pricelist, id_product, id_variation);');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
/** Creating pricelists table */
|
||||
public function upgrade_tableExists()
|
||||
{
|
||||
sqlQuery('CREATE TABLE pricelists (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
currency VARCHAR(3) DEFAULT NULL,
|
||||
name VARCHAR(60) NOT NULL,
|
||||
INDEX IDX_28511D046956883F (currency),
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE = InnoDB');
|
||||
|
||||
sqlQuery('ALTER TABLE pricelists ADD CONSTRAINT FK_28511D046956883F FOREIGN KEY (currency) REFERENCES currencies (id)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
/** Creating pricelists_products table */
|
||||
public function upgrade_tablePricelistsProductsExists()
|
||||
{
|
||||
sqlQuery('CREATE TABLE pricelists_products (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
id_pricelist INT NOT NULL,
|
||||
id_product INT NOT NULL,
|
||||
id_variation INT DEFAULT NULL,
|
||||
price NUMERIC(15, 4) DEFAULT NULL COMMENT \'(DC2Type:decimal)\',
|
||||
discount NUMERIC(12, 8) DEFAULT NULL COMMENT \'(DC2Type:decimal)\',
|
||||
INDEX IDX_49750F94DCFA564A (id_pricelist),
|
||||
CONSTRAINT FK_49750F94DCFA564A FOREIGN KEY (id_pricelist) REFERENCES pricelists (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(id)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB;');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_emptyPricelistsProducts()
|
||||
{
|
||||
return sqlQueryBuilder()->select('count(id)')
|
||||
->from('pricelists_products')
|
||||
->where('price IS NULL AND discount IS NULL')
|
||||
->execute()->fetchColumn() > 0;
|
||||
}
|
||||
|
||||
/** Delete empty pricelists_products */
|
||||
public function upgrade_emptyPricelistsProducts()
|
||||
{
|
||||
sqlQueryBuilder()->delete('pricelists_products')
|
||||
->where('price IS NULL AND discount IS NULL')
|
||||
->execute();
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_integrityUnique()
|
||||
{
|
||||
return $this->checkConstraintExists('pricelists_products', 'integrity_unique');
|
||||
}
|
||||
|
||||
/** Add virtual column to check uniqueness even for NULL variations */
|
||||
public function upgrade_integrityUnique()
|
||||
{
|
||||
try {
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD integrity_unique varchar(50) as (CONCAT_WS("-", `id_pricelist`, `id_product`, `id_variation`)) VIRTUAL');
|
||||
} catch (Exception $e) {
|
||||
// Ignore exception if column already exists - caused by failing DELETE statement
|
||||
}
|
||||
sqlQuery('DELETE pp FROM pricelists_products pp WHERE pp.id NOT IN (SELECT t.* FROM (SELECT MAX(pp2.id) FROM pricelists_products pp2 GROUP BY pp2.integrity_unique) t)');
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD UNIQUE integrity_unique(integrity_unique)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_ProductForeignKey(): bool
|
||||
{
|
||||
return $this->checkConstraintWithColumnExists('pricelists_products', 'FK_pricelists_products_id_product', 'id_product');
|
||||
}
|
||||
|
||||
/** pricelist_products: add foreign key on id_product and id_variation columns */
|
||||
public function upgrade_ProductForeignKey(): void
|
||||
{
|
||||
// Smazat zaznamy na neexistujici produkty
|
||||
sqlQuery('DELETE FROM pricelists_products WHERE id_product NOT IN (SELECT id FROM products)');
|
||||
sqlQuery('DELETE FROM pricelists_products WHERE id_variation IS NOT NULL AND id_variation NOT IN (SELECT id FROM products_variations)');
|
||||
|
||||
// Vytvorim foreign keys
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD CONSTRAINT `FK_pricelists_products_id_product`
|
||||
FOREIGN KEY (id_product) REFERENCES products (id) ON DELETE CASCADE ON UPDATE CASCADE');
|
||||
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD CONSTRAINT `FK_pricelists_products_id_variation`
|
||||
FOREIGN KEY (id_variation) REFERENCES products_variations (id) ON DELETE CASCADE ON UPDATE CASCADE');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_PricelistsToken()
|
||||
{
|
||||
return findModule(Modules::PRICELISTS, Modules::SUB_ACTIVATION_TOKEN) && $this->checkColumnExists('pricelists', 'token');
|
||||
}
|
||||
|
||||
/** Add `token` column to `pricelists` */
|
||||
public function upgrade_PricelistsToken()
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists ADD COLUMN token varchar(255)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_PriceListPriceHistory(): bool
|
||||
{
|
||||
return findModule(Modules::PRICE_HISTORY) && $this->checkColumnExists('pricelists', 'price_history');
|
||||
}
|
||||
|
||||
/** Add pricelists.price_history column */
|
||||
public function upgrade_PriceListPriceHistory(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists ADD COLUMN price_history TINYINT DEFAULT 0');
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD COLUMN price_for_discount DECIMAL(15, 4) DEFAULT NULL AFTER price');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_extendProductDiscount(): bool
|
||||
{
|
||||
return $this->checkColumnExists('pricelists', 'use_product_discount');
|
||||
}
|
||||
|
||||
/** Add pricelists.use_product_discount column */
|
||||
public function upgrade_extendProductDiscount(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists ADD COLUMN use_product_discount TINYINT DEFAULT 0');
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_coefficient(): bool
|
||||
{
|
||||
return $this->checkColumnExists('pricelists', 'coefficient');
|
||||
}
|
||||
|
||||
/** Add pricelists.coefficient column */
|
||||
public function upgrade_coefficient(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists ADD COLUMN coefficient DECIMAL(4,2) DEFAULT NULL');
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_generated(): bool
|
||||
{
|
||||
return $this->checkColumnExists('pricelists_products', 'generated');
|
||||
}
|
||||
|
||||
/** Add pricelists_products.generated column */
|
||||
public function upgrade_generated(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists_products ADD COLUMN generated TINYINT DEFAULT 0');
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_PriceListDataColumn(): bool
|
||||
{
|
||||
return $this->checkColumnExists('pricelists', 'data');
|
||||
}
|
||||
|
||||
/** add `pricelists.data` column */
|
||||
public function upgrade_PriceListDataColumn(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE pricelists ADD COLUMN data MEDIUMTEXT DEFAULT NULL');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\PricelistBundle\Resources\upgrade;
|
||||
|
||||
class UserUpgrade extends \UpgradeNew
|
||||
{
|
||||
protected $priority = 1110;
|
||||
|
||||
public function check_PriceListId(): bool
|
||||
{
|
||||
return $this->checkColumnExists('users', 'id_pricelist');
|
||||
}
|
||||
|
||||
/** users: add id_pricelist column */
|
||||
public function upgrade_PriceListId(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE users ADD COLUMN id_pricelist INT(11)');
|
||||
sqlQuery('ALTER TABLE users ADD FOREIGN KEY (id_pricelist) REFERENCES pricelists(id) ON UPDATE CASCADE ON DELETE SET NULL');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_UserGroupsPriceListId(): bool
|
||||
{
|
||||
return $this->checkColumnExists('users_groups', 'id_pricelist');
|
||||
}
|
||||
|
||||
/** Add `users_groups.id_pricelist` */
|
||||
public function upgrade_UserGroupsPriceListId(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE users_groups ADD COLUMN id_pricelist INT DEFAULT NULL;');
|
||||
sqlQuery('ALTER TABLE users_groups ADD CONSTRAINT users_groups_id_pricelists FOREIGN KEY (id_pricelist) REFERENCES pricelists(id) ON UPDATE CASCADE ON DELETE CASCADE;');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_UserGroupsPriceListConstraint(): bool
|
||||
{
|
||||
return $this->checkConstraintRule('users_groups', 'users_groups_id_pricelists', 'SET NULL', 'CASCADE');
|
||||
}
|
||||
|
||||
public function upgrade_UserGroupsPriceListConstraint(): void
|
||||
{
|
||||
try {
|
||||
sqlQuery('ALTER TABLE users_groups DROP CONSTRAINT users_groups_id_pricelists;');
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
sqlQuery('ALTER TABLE users_groups ADD CONSTRAINT users_groups_id_pricelists FOREIGN KEY (id_pricelist) REFERENCES pricelists(id) ON UPDATE CASCADE ON DELETE SET NULL;');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"pricelists" : [
|
||||
{
|
||||
"id" : 4,
|
||||
"currency" : "EUR",
|
||||
"name" : "TEST_EUR",
|
||||
"price_history" : 1,
|
||||
"use_product_discount" : 1,
|
||||
"coefficient" : null,
|
||||
"data" : null
|
||||
}
|
||||
],
|
||||
"pricelists_products" : [
|
||||
{
|
||||
"id" : 14,
|
||||
"id_pricelist" : 4,
|
||||
"id_product" : 11,
|
||||
"id_variation" : null,
|
||||
"price" : 499.9000,
|
||||
"price_for_discount" : 499.9000,
|
||||
"discount" : 15.00000000,
|
||||
"integrity_unique" : "4-11",
|
||||
"generated" : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Tests;
|
||||
|
||||
use KupShop\PricelistBundle\Util\PriceListWorker;
|
||||
use Query\Operator;
|
||||
|
||||
class PriceListWorkerTest extends \DatabaseTestCase
|
||||
{
|
||||
private PriceListWorker $priceListWorker;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->priceListWorker = $this->get(PriceListWorker::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data_testWithoutDiscountUpdatePricelist
|
||||
*/
|
||||
public function testWithoutDiscountUpdatePricelist(bool $withDiscountUpdate, \Decimal $discount, array $result)
|
||||
{
|
||||
$pricelistId = 4;
|
||||
$productId = 11;
|
||||
$variationId = null;
|
||||
$price = toDecimal(449);
|
||||
|
||||
$this->priceListWorker->updatePriceList($pricelistId, $productId, $variationId, $price, $discount, $withDiscountUpdate);
|
||||
$pricelistProduct = $this->getPricelistProduct($pricelistId, $productId, $variationId)[0];
|
||||
|
||||
$this->assertEquals($result, $pricelistProduct);
|
||||
}
|
||||
|
||||
public function data_testWithoutDiscountUpdatePricelist()
|
||||
{
|
||||
// nemelo by updatovat slevu - zustane 15%
|
||||
yield 'WithDiscountUpdateFalse' => [false, toDecimal(0), [
|
||||
'id_pricelist' => 4,
|
||||
'id_product' => 11,
|
||||
'id_variation' => null,
|
||||
'price' => '449.0000',
|
||||
'price_for_discount' => '499.9000',
|
||||
'discount' => '15.00000000',
|
||||
]];
|
||||
|
||||
// melo by updatovat slevu - prepise se na null
|
||||
yield 'WithDiscountUpdateTrue' => [true, toDecimal(0), [
|
||||
'id_pricelist' => 4,
|
||||
'id_product' => 11,
|
||||
'id_variation' => null,
|
||||
'price' => '449.0000',
|
||||
'price_for_discount' => '499.9000',
|
||||
'discount' => null,
|
||||
]];
|
||||
}
|
||||
|
||||
protected function getPricelistProduct(int $priceListId, int $productId, ?int $variationId)
|
||||
{
|
||||
return sqlQueryBuilder()
|
||||
->select('id_pricelist, id_product, id_variation, price, price_for_discount, discount')
|
||||
->from('pricelists_products')
|
||||
->where(Operator::equalsNullable(['id_pricelist' => $priceListId, 'id_product' => $productId, 'id_variation' => $variationId]))
|
||||
->execute()
|
||||
->fetchAllAssociative();
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->getJsonDataSetFromFile();
|
||||
}
|
||||
}
|
||||
303
bundles/KupShop/PricelistBundle/Tests/PricelistDiscountTest.json
Normal file
303
bundles/KupShop/PricelistBundle/Tests/PricelistDiscountTest.json
Normal file
@@ -0,0 +1,303 @@
|
||||
{
|
||||
"currencies": [
|
||||
{
|
||||
"id": "CZK",
|
||||
"name": "Česká koruna",
|
||||
"symbol": "Kč",
|
||||
"price_round": 100,
|
||||
"price_round_order": 0,
|
||||
"price_round_direction": "math",
|
||||
"price_precision": "0",
|
||||
"price_decimal_mark": ",",
|
||||
"rate": 1.00000000,
|
||||
"sync": "N",
|
||||
"corrections": 0.00,
|
||||
"sync_date_update": null
|
||||
}
|
||||
],
|
||||
"products": [
|
||||
{
|
||||
"id": 44802,
|
||||
"title": "16GB USB ADATA C008 bílo/modrá (potisk)",
|
||||
"code": "RQZA000101",
|
||||
"ean": 4718050609642,
|
||||
"short_descr": "",
|
||||
"long_descr": "<br>* Elegantní a praktický USB flash drive. <br>* Vhodné pro potisk <br>* Kapacita - 16GB <br>* Barva/design - Modro-bílá <br>* Rozměry - 59,95x19,83x8,85 mm <br>* Hmotnost - 10g <br>* Rozhraní - USB 2.0 a kompatibilní i s USB 1.1 <br>* Příslušenství - poutko na pověšení <br>* Systémové požadavky - Windows 98/98SE/ME/2000/XP/Vista™/7 nebo vyšší, Mac OS 9.0 nebo vyšší, Linux Kernel 2.4 nebo vyšší <br>* Zdarma aplikace: <br>* UFDtoGO - nástroj pro obsluhu Vaseho USB Flash Drive <br>* Norton Internet Security (60 -dní trial) - anitvirus Více informací na stránkách výrobce: <br>* http://www.adata.com.tw/index_en.html",
|
||||
"parameters": "",
|
||||
"price": 77.6860,
|
||||
"price_for_discount": 172.7273,
|
||||
"price_common": 0.0000,
|
||||
"vat": 1,
|
||||
"discount": 2.00000000,
|
||||
"producer": null,
|
||||
"guarantee": 0,
|
||||
"in_store": 2,
|
||||
"pieces_sold": 0,
|
||||
"delivery_time": 0,
|
||||
"campaign": "",
|
||||
"updated": "2023-08-04 01:21:01",
|
||||
"date_added": "2023-06-30 15:29:18",
|
||||
"figure": "Y",
|
||||
"show_raw_price": "N",
|
||||
"position": null,
|
||||
"meta_title": null,
|
||||
"meta_description": null,
|
||||
"meta_keywords": null,
|
||||
"show_in_feed": "Y",
|
||||
"max_cpc": 0,
|
||||
"in_store_min": null,
|
||||
"note": "AC008-16G-RWE",
|
||||
"weight": null,
|
||||
"data": "{\"in_store_30\":\"2\"}",
|
||||
"bonus_points": null,
|
||||
"date_stock_in": null,
|
||||
"price_buy": 68.0000,
|
||||
"show_in_search": "Y",
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null
|
||||
},
|
||||
{
|
||||
"id": 1728,
|
||||
"title": "70Mai Dash Cam 1S",
|
||||
"code": "1BI3000101",
|
||||
"ean": 6971669780463,
|
||||
"short_descr": "Kamera do auta, 1080p při 30 fps, úhel záběru 130 stupňů, kvalitní snímač Sony IMX323",
|
||||
"long_descr": "<h3>Mi Dash Cam 70 Mai 1S</h3>\r\n\r\n<p>Hledáte kameru do auta, která nebude stát mnoho peněz a přesto dokáže nabídnout vysokou kvalitu pořízeného záznamu? Právě jste ji našli - Mi Dash Cam 70 Mai 1S nabízí kromě uvedených vlastností ještě celou řadu zajímavých parametrů.</p>\r\n\r\n<p style=\"text-align:center\"><img alt=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" src=\"/data/photos/old/product-texts/xiaomi-mi-dash-cam-70-mai-1s-3-jpg.jpg\" style=\"height:606px; width:800px\" title=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" /></p>\r\n\r\n<p>Kvalitu záznamu garantuje osvědčený snímač Sony IMX323 s deklarovanou světelností f/2,2, díky které dokáže pořizovat hezký obraz i za zhoršených světelných podmínek. Zaznamenané video má vysoké rozlišení 1080p, tedy 1920 × 1080 obrazových bodů, což je zárukou zachycení dostatečných detailů.</p>\r\n\r\n<h3>Co umí chytrá kamera do auta?</h3>\r\n\r\n<p>Kameru nainstalujete za čelní sklo a její objektiv bude snímat dění před vozidlem. Díky širokému úhlu záběru 130 stupňů natáčí situaci v dostatečné perspektivě. Záznam videa je ukládán na paměťovou kartu - ta není součástí balení a doporučujeme používat karty microSD alespoň třídy Class 10.</p>\r\n\r\n<p style=\"text-align:center\"><img alt=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" src=\"/data/photos/old/product-texts/xiaomi-mi-dash-cam-70-mai-1s-1-jpg.jpg\" style=\"height:465px; width:960px\" title=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" /></p>\r\n\r\n<p>Kamera Mi Dash Cam 70 Mai 1S podporuje karty s kapacitou 16, 32, nebo 64 GB, což postačuje pro uložení mnoha hodin záznamu v nejvyšší kvalitě. V případě, že dojde k zaplnění karty, začne kamera automaticky přepisovat nejstarší záznamy.</p>\r\n\r\n<p style=\"text-align:center\"><img alt=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" src=\"/data/photos/old/product-texts/xiaomi-mi-dash-cam-70-mai-1s-5-jpg.jpg\" style=\"height:537px; width:960px\" title=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" /></p>\r\n\r\n<p>Zařízení je vybaveno snímačem zrychlení - v případě, že kamera zaznamená prudké brzdění nebo náraz, automaticky uloží předcházející záznam pro případné snazší dokazování vzniku nehody. Ke kameře se lze připojit přes aplikaci v mobilním telefonu a spravovat, stahovat a sdílet pořízená videa.</p>\r\n\r\n<p style=\"text-align:center\"><img alt=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" src=\"/data/photos/old/product-texts/xiaomi-mi-dash-cam-70-mai-1s-4-jpg.jpg\" style=\"height:693px; width:960px\" title=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" /></p>\r\n\r\n<h3>Vlastnosti Mi Dash Cam 70 Mai 1S</h3>\r\n\r\n<ul>\r\n\t<li>Název: Mi Dash Cam 70 Mai 1S</li>\r\n\t<li>Typ zařízení: kamera do auta/„černá skříňka“</li>\r\n\t<li>Snímač: Sony IMX323</li>\r\n\t<li>Rozlišení videa: 1080p - 1920 × 1080 obrazových bodů</li>\r\n\t<li>Úhel záběru: 130 stupňů</li>\r\n\t<li>Procesor: MSC8328P</li>\r\n\t<li>Úložiště: paměťová karta microSD (není součástí balení)</li>\r\n\t<li>Komunikace s telefonem: ano, přes aplikaci, Wi-Fi</li>\r\n</ul>\r\n\r\n<p style=\"text-align:center\"><img alt=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" src=\"/data/photos/old/product-texts/xiaomi-mi-dash-cam-70-mai-1s-2-jpg.jpg\" style=\"height:716px; width:1000px\" title=\"Kamera do auta Xiaomi Mi Dash Cam 70 Mai 1S\" /></p>\r\n\r\n<h3>Obsah balení</h3>\r\n\r\n<ul>\r\n\t<li>1× kamera Mi Dash Cam 70 Mai 1S</li>\r\n\t<li>1× napájecí kabel dlouhý cca 3,5 metru</li>\r\n\t<li>1× uživatelská příručka</li>\r\n</ul>",
|
||||
"parameters": "<h3>Vlastnosti Mi Dash Cam 70 Mai 1S</h3>\r\n\r\n<ul>\r\n\t<li>Název: Mi Dash Cam 70 Mai 1S</li>\r\n\t<li>Typ zařízení: kamera do auta/„černá skříňka“</li>\r\n\t<li>Snímač: Sony IMX323</li>\r\n\t<li>Rozlišení videa: 1080p - 1920 × 1080 obrazových bodů</li>\r\n\t<li>Úhel záběru: 130 stupňů</li>\r\n\t<li>Procesor: MSC8328P</li>\r\n\t<li>Úložiště: paměťová karta microSD (není součástí balení)</li>\r\n\t<li>Komunikace s telefonem: ano, přes aplikaci, Wi-Fi</li>\r\n</ul>\r\n\r\n<h3>Obsah balení</h3>\r\n\r\n<ul>\r\n\t<li>1× kamera Mi Dash Cam 70 Mai 1S</li>\r\n\t<li>1× napájecí kabel dlouhý cca 3,5 metru</li>\r\n\t<li>1× uživatelská příručka</li>\r\n</ul>",
|
||||
"price": 794.2149,
|
||||
"price_for_discount": 769.4215,
|
||||
"price_common": 0.0000,
|
||||
"vat": 1,
|
||||
"discount": 20.00000000,
|
||||
"producer": null,
|
||||
"guarantee": 0,
|
||||
"in_store": 8,
|
||||
"pieces_sold": 1071,
|
||||
"delivery_time": 0,
|
||||
"campaign": "POP",
|
||||
"updated": "2023-08-04 03:06:20",
|
||||
"date_added": "2022-07-07 00:00:00",
|
||||
"figure": "Y",
|
||||
"show_raw_price": "N",
|
||||
"position": null,
|
||||
"meta_title": "Mi Dash Cam 70 Mai 1S - skvělá kamera bez kompromisů",
|
||||
"meta_description": "Mi Dash Cam 70 Mai 1S",
|
||||
"meta_keywords": "yi 4k action camera,akční kamera yi,xiaomi akční kamera,kit,voděodolné pouzdro",
|
||||
"show_in_feed": "Y",
|
||||
"max_cpc": 0,
|
||||
"in_store_min": null,
|
||||
"note": "",
|
||||
"weight": 0.000000,
|
||||
"data": "{\"abra\":{\"1BI3000101\":{\"in_store\":{\"1\":\"0\",\"7\":\"0\",\"3\":\"0\",\"2\":\"0\",\"6\":\"0\",\"10\":\"1\"},\"on_the_way_count\":\"0\",\"on_the_way_date\":\"26.07.2021\"}},\"generate_coupon\":\"N\",\"generate_coupon_discount\":\"4\",\"in_store_01\":\"8.000000\",\"in_store_20\":\"0\",\"in_store_30\":\"0\"}",
|
||||
"bonus_points": null,
|
||||
"date_stock_in": null,
|
||||
"price_buy": 659.0000,
|
||||
"show_in_search": "Y",
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null
|
||||
},
|
||||
{
|
||||
"id": 306,
|
||||
"title": "Originální TPU obal pro Xiaomi Mi Max",
|
||||
"code": "master_TMMH000101",
|
||||
"ean": null,
|
||||
"short_descr": "",
|
||||
"long_descr": "<p><strong>Originální TPU obal pro Xiaomi Mi Max</strong></p>\r\n\r\n<p>Máte strach, že váš telefon utrží nepříjemné rány a škrábance při denním nošení? Co jej ochránit speciálním obalem z tvrdého materiálu, který udrží váš telefon v bezpečí? Svou odolností zamezí mechanickému poškození, je velmi příjemný na dotek a získáte navíc pevnější úchyt telefonu. Povedená konstrukce ani neruší design přístroje, obal nebrání pohodlnému ovládání a zároveň nezhoršuje funkce telefonu. Aplikace obalu je snadná, zároveň ale pevně drží a samovolně se z přístroje rozhodně neuvolní.</p>\r\n\r\n<p><img alt=\"Xiaomi Mi Max TPU\" src=\"/data/photos/old/product-texts/xiaomi-20mi-20max-20tpu-20-20pink-jpg.jpg\" style=\"height:363px; margin-left:auto; margin-right:auto; width:500px\" title=\"Xiaomi Mi Max TPU\" /> </p>\r\n\r\n<p><strong>Detail</strong></p>\r\n\r\n<ul>\r\n\t<li>ochrání tělo telefonu před mechanickým poškozením</li>\r\n\t<li>ideální ochrana pro telefon Xiaomi Mi Max</li>\r\n</ul>\r\n\r\n<p> </p>\r\n\r\n<p><strong>Obsah balení</strong></p>\r\n\r\n<ul>\r\n\t<li>TPU obal pro Xiaomi Redmi Mi Max</li>\r\n</ul>",
|
||||
"parameters": "<p><strong>Detail</strong></p>\r\n\r\n<ul>\r\n\t<li>ochrání tělo telefonu před mechanickým poškozením</li>\r\n\t<li>ideální ochrana pro telefon Xiaomi Mi Max</li>\r\n</ul>\r\n\r\n<p> </p>\r\n\r\n<p><strong>Obsah balení</strong></p>\r\n\r\n<ul>\r\n\t<li>TPU obal pro Xiaomi Redmi Mi Max</li>\r\n</ul>",
|
||||
"price": 81.8182,
|
||||
"price_for_discount": 81.8182,
|
||||
"price_common": 0.0000,
|
||||
"vat": 1,
|
||||
"discount": 33.00000000,
|
||||
"producer": null,
|
||||
"guarantee": 0,
|
||||
"in_store": 6,
|
||||
"pieces_sold": 65,
|
||||
"delivery_time": 0,
|
||||
"campaign": "",
|
||||
"updated": "2023-08-07 15:17:58",
|
||||
"date_added": "2022-07-07 00:00:00",
|
||||
"figure": "Y",
|
||||
"show_raw_price": "N",
|
||||
"position": null,
|
||||
"meta_title": null,
|
||||
"meta_description": null,
|
||||
"meta_keywords": "",
|
||||
"show_in_feed": "Y",
|
||||
"max_cpc": 0,
|
||||
"in_store_min": null,
|
||||
"note": null,
|
||||
"weight": 0.000000,
|
||||
"data": "{\"abra\":{\"5F60000101\":{\"in_store\":{\"5\":\"0\",\"1\":\"3\",\"6\":\"0\"}},\"1F60000101\":{\"in_store\":{\"5\":\"0\",\"7\":\"0\",\"1\":\"1\",\"6\":\"0\",\"3\":\"0\"}},\"4F60000101\":{\"in_store\":{\"5\":\"0\",\"1\":\"0\",\"6\":\"0\"}},\"3F60000101\":{\"in_store\":{\"5\":\"0\",\"1\":\"4\",\"6\":\"0\"}},\"2F60000101\":{\"in_store\":{\"5\":\"0\",\"1\":\"2\",\"6\":\"0\",\"3\":\"0\"}}},\"generate_coupon\":\"N\",\"generate_coupon_discount\":\"4\"}",
|
||||
"bonus_points": null,
|
||||
"date_stock_in": null,
|
||||
"price_buy": null,
|
||||
"show_in_search": "Y",
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null
|
||||
}
|
||||
],
|
||||
"products_variations": [
|
||||
{
|
||||
"id": 1455,
|
||||
"id_product": 306,
|
||||
"code": "2F60000101",
|
||||
"ean": 8595633307982,
|
||||
"title": "Barva: Černá",
|
||||
"in_store": 0,
|
||||
"delivery_time": 0,
|
||||
"price": 81.8182,
|
||||
"price_for_discount": 81.8182,
|
||||
"figure": "Y",
|
||||
"updated": "2023-05-11 11:26:10",
|
||||
"note": null,
|
||||
"weight": null,
|
||||
"bonus_points": null,
|
||||
"price_buy": 0.0000,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null,
|
||||
"data": "{\"in_store_01\":\"0.000000\"}",
|
||||
"price_common": 0.0000
|
||||
},
|
||||
{
|
||||
"id": 1456,
|
||||
"id_product": 306,
|
||||
"code": "1F60000101",
|
||||
"ean": 8595633307999,
|
||||
"title": "Barva: Průhledná",
|
||||
"in_store": 1,
|
||||
"delivery_time": 0,
|
||||
"price": 189.2562,
|
||||
"price_for_discount": 81.8182,
|
||||
"figure": "Y",
|
||||
"updated": "2023-05-26 15:42:07",
|
||||
"note": null,
|
||||
"weight": null,
|
||||
"bonus_points": null,
|
||||
"price_buy": 100.0000,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null,
|
||||
"data": "{\"in_store_01\":\"1.000000\"}",
|
||||
"price_common": 0.0000
|
||||
},
|
||||
{
|
||||
"id": 1457,
|
||||
"id_product": 306,
|
||||
"code": "4F60000101",
|
||||
"ean": 8595633308019,
|
||||
"title": "Barva: Růžová",
|
||||
"in_store": 0,
|
||||
"delivery_time": 0,
|
||||
"price": 247.1074,
|
||||
"price_for_discount": 247.1074,
|
||||
"figure": "Y",
|
||||
"updated": "2023-05-11 11:26:10",
|
||||
"note": null,
|
||||
"weight": null,
|
||||
"bonus_points": null,
|
||||
"price_buy": 0.0000,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null,
|
||||
"data": "{\"in_store_01\":\"0.000000\"}",
|
||||
"price_common": 0.0000
|
||||
},
|
||||
{
|
||||
"id": 1458,
|
||||
"id_product": 306,
|
||||
"code": "3F60000101",
|
||||
"ean": 8595633308002,
|
||||
"title": "Barva: Modrá",
|
||||
"in_store": 2,
|
||||
"delivery_time": 0,
|
||||
"price": 247.1074,
|
||||
"price_for_discount": 81.8182,
|
||||
"figure": "Y",
|
||||
"updated": "2023-07-27 03:02:05",
|
||||
"note": null,
|
||||
"weight": null,
|
||||
"bonus_points": null,
|
||||
"price_buy": 151.0000,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null,
|
||||
"data": "{\"in_store_01\":\"2.000000\"}",
|
||||
"price_common": 0.0000
|
||||
},
|
||||
{
|
||||
"id": 1459,
|
||||
"id_product": 306,
|
||||
"code": "5F60000101",
|
||||
"ean": 8595633308026,
|
||||
"title": "Barva: Žlutá",
|
||||
"in_store": 3,
|
||||
"delivery_time": 0,
|
||||
"price": 222.3140,
|
||||
"price_for_discount": 81.8182,
|
||||
"figure": "Y",
|
||||
"updated": "2023-05-26 15:42:07",
|
||||
"note": null,
|
||||
"weight": null,
|
||||
"bonus_points": null,
|
||||
"price_buy": 134.0000,
|
||||
"width": null,
|
||||
"height": null,
|
||||
"depth": null,
|
||||
"data": "{\"in_store_01\":\"3.000000\"}",
|
||||
"price_common": 0.0000
|
||||
}
|
||||
],
|
||||
"pricelists" : [
|
||||
{
|
||||
"id": 1,
|
||||
"currency": "CZK",
|
||||
"name": "CZK ceník",
|
||||
"price_history": 0,
|
||||
"use_product_discount": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"currency": "CZK",
|
||||
"name": "CZK ceník - bez přeráření",
|
||||
"price_history": 0,
|
||||
"use_product_discount": 0
|
||||
}
|
||||
],
|
||||
"pricelists_products" : [
|
||||
{
|
||||
"id": 1,
|
||||
"id_pricelist": 1,
|
||||
"id_product": 44802,
|
||||
"id_variation": null,
|
||||
"price": 41.5,
|
||||
"price_for_discount": null,
|
||||
"discount": 5.00000000,
|
||||
"integrity_unique": "1-44802"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"id_pricelist": 2,
|
||||
"id_product": 44802,
|
||||
"id_variation": null,
|
||||
"price": 41.5,
|
||||
"price_for_discount": null,
|
||||
"discount": 5.00000000,
|
||||
"integrity_unique": "1-44802"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"id_pricelist": 1,
|
||||
"id_product": 306,
|
||||
"id_variation": 1456,
|
||||
"price": null,
|
||||
"price_for_discount": null,
|
||||
"discount": 80.00000000,
|
||||
"integrity_unique": "1-306-1456"
|
||||
}
|
||||
]
|
||||
}
|
||||
145
bundles/KupShop/PricelistBundle/Tests/PricelistDiscountTest.php
Normal file
145
bundles/KupShop/PricelistBundle/Tests/PricelistDiscountTest.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Tests;
|
||||
|
||||
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
||||
use KupShop\OrderingBundle\Cart;
|
||||
use KupShop\PricelistBundle\Context\PricelistContext;
|
||||
|
||||
class PricelistDiscountTest extends \DatabaseTestCase
|
||||
{
|
||||
use CartTestTrait;
|
||||
|
||||
/** @var PricelistContext */
|
||||
private $pricelistContext;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->pricelistContext = $this->get(PricelistContext::class);
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->getJsonDataSetFromFile();
|
||||
}
|
||||
|
||||
public function testProductWithPricelistDiscountAndEnabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(1);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(44802);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
// Kontrola nastavené slevy
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(5.0, $product->discount->asFloat());
|
||||
|
||||
// Cena je v eurech načtená z ceníků
|
||||
$this->assertEquals(50, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(48, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(48, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
|
||||
public function testProductWithPricelistDiscountAndDisabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(2);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(44802);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
// Kontrola nastavené slevy
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(5.0, $product->discount->asFloat());
|
||||
|
||||
// Cena je v eurech načtená z ceníků
|
||||
$this->assertEquals(50, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(48, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(48, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
|
||||
public function testProductWithouPricelistDiscountAndDisabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(2);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(1728);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(0.0, $product->discount->asFloat());
|
||||
|
||||
$this->assertEquals(961, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(961, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(961, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
|
||||
public function testProductWithouPricelistDiscountAndEnabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(1);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(1728);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(20.0, $product->discount->asFloat());
|
||||
|
||||
$this->assertEquals(961, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(769, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(769, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
|
||||
public function testVariantWithouPricelistDiscountAndEnabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(1);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(306, 1455);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(33.0, $product->discount->asFloat());
|
||||
|
||||
$this->assertEquals(99, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(66, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(66, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
|
||||
public function testVariantWithPricelistDiscountAndEnabledExtension()
|
||||
{
|
||||
$this->pricelistContext->activate(1);
|
||||
|
||||
$this->cart = $this->get(Cart::class);
|
||||
$this->createCart();
|
||||
$this->insertProduct(306, 1456);
|
||||
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$product = reset($this->cart->products)['product'];
|
||||
$this->assertEquals(80.0, $product->discount->asFloat());
|
||||
|
||||
$this->assertEquals(229, $product->getProductPrice()->getPriceWithVatWithoutDiscount()->asFloat());
|
||||
$this->assertEquals(46, $product->getProductPrice()->getPriceWithVat()->asFloat());
|
||||
|
||||
$this->assertEquals(46, $this->cart->totalPricePay->asFloat());
|
||||
}
|
||||
}
|
||||
126
bundles/KupShop/PricelistBundle/Tests/PricelistTest.json
Normal file
126
bundles/KupShop/PricelistBundle/Tests/PricelistTest.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"pricelists" : [
|
||||
{
|
||||
"id":1,
|
||||
"currency":"CZK",
|
||||
"name":"PricelistCZK",
|
||||
"coefficient":null
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"currency":"EUR",
|
||||
"name":"PricelistEUR",
|
||||
"coefficient":null
|
||||
}
|
||||
],
|
||||
"pricelists_products" : [
|
||||
{
|
||||
"id":1,
|
||||
"id_pricelist":1,
|
||||
"id_product":1,
|
||||
"id_variation":null,
|
||||
"price":1000,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"id_pricelist":2,
|
||||
"id_product":1,
|
||||
"id_variation":null,
|
||||
"price":38.5,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":3,
|
||||
"id_pricelist":1,
|
||||
"id_product":4,
|
||||
"id_variation":1,
|
||||
"price":260,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":8,
|
||||
"id_pricelist":1,
|
||||
"id_product":4,
|
||||
"id_variation":2,
|
||||
"price":260,
|
||||
"discount":5,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":4,
|
||||
"id_pricelist":2,
|
||||
"id_product":4,
|
||||
"id_variation":null,
|
||||
"price":10,
|
||||
"discount":10,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":9,
|
||||
"id_pricelist":2,
|
||||
"id_product":4,
|
||||
"id_variation":2,
|
||||
"price":10,
|
||||
"discount":20,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":5,
|
||||
"id_pricelist":1,
|
||||
"id_product":3,
|
||||
"id_variation":null,
|
||||
"price":260,
|
||||
"discount":10,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":6,
|
||||
"id_pricelist":2,
|
||||
"id_product":3,
|
||||
"id_variation":null,
|
||||
"price":10,
|
||||
"discount":10,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":7,
|
||||
"id_pricelist":2,
|
||||
"id_product":6,
|
||||
"id_variation":null,
|
||||
"price":null,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":10,
|
||||
"id_pricelist":2,
|
||||
"id_product":11,
|
||||
"id_variation":22,
|
||||
"price":10,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":11,
|
||||
"id_pricelist":2,
|
||||
"id_product":10,
|
||||
"id_variation":null,
|
||||
"price":20,
|
||||
"discount":null,
|
||||
"generated":0
|
||||
},
|
||||
{
|
||||
"id":12,
|
||||
"id_pricelist":2,
|
||||
"id_product":9,
|
||||
"id_variation":null,
|
||||
"price":null,
|
||||
"discount":25,
|
||||
"generated":0
|
||||
}
|
||||
]
|
||||
}
|
||||
308
bundles/KupShop/PricelistBundle/Tests/PricelistTest.php
Normal file
308
bundles/KupShop/PricelistBundle/Tests/PricelistTest.php
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Tests;
|
||||
|
||||
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
||||
use KupShop\KupShopBundle\Context\CurrencyContext;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\KupShopBundle\Util\Contexts;
|
||||
use KupShop\OrderingBundle\Util\CartFactory;
|
||||
use KupShop\PricelistBundle\Context\PricelistContext;
|
||||
use KupShop\PricelistBundle\Entity\Pricelist;
|
||||
|
||||
class PricelistTest extends \DatabaseTestCase
|
||||
{
|
||||
use CartTestTrait;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->createCart();
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/*
|
||||
* HACK
|
||||
* Prazdny test - prvni test vzdycky spadl s chybou:
|
||||
* No database selected
|
||||
* nevime proc, ale tohle to fixnulo
|
||||
*/
|
||||
public function testFixSetUp()
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testPricelistEurPrice()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
$currencyContext->activate('EUR');
|
||||
|
||||
$productList = new \ProductList();
|
||||
|
||||
$products = $productList->getProducts();
|
||||
|
||||
$this->assertEquals('46,60 €', $products[1]->price);
|
||||
$this->assertEquals('115,40 €', $products[6]->price);
|
||||
}
|
||||
|
||||
public function testPricelistCzkPrice()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$pricelistContext->activate(1);
|
||||
|
||||
$productList = new \ProductList();
|
||||
|
||||
$product = $productList->getProducts()[1];
|
||||
|
||||
$this->assertEquals('1 210 Kč', $product->price);
|
||||
}
|
||||
|
||||
public function testPricelistCzkVariationPrice()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$pricelistContext->activate(1);
|
||||
|
||||
$productList = new \ProductList();
|
||||
$productList->fetchVariations(null);
|
||||
$product = $productList->getProducts()[4];
|
||||
|
||||
$this->assertEquals('315 Kč', $product->price);
|
||||
|
||||
$variation = $product->variations[1];
|
||||
$this->assertEquals('315 Kč', printPrice($variation['price']));
|
||||
$variation = $product->variations[2];
|
||||
$this->assertEquals('299 Kč', printPrice($variation['price'])); // 315 - 5% (variation pricelist discount)
|
||||
}
|
||||
|
||||
public function testPricelistEurVariationPrice()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
$currencyContext->activate('EUR');
|
||||
|
||||
$productList = new \ProductList();
|
||||
$productList->fetchVariations(null);
|
||||
$product = $productList->getProducts()[4];
|
||||
|
||||
// Zakomentováno, varianty nemají na sobě cenu, a tak se nedokáže správně dofetchovat na produkt správná pricelist cena se slevou.
|
||||
// Vzhledem ke komplexnosti problému a jeho malému use-casu jsme se to rozhodli neřešit.
|
||||
// $this->assertEquals('9,70 €', $product->price); // 12,10 - 20% (variation pricelist discount from cheapest variation)
|
||||
|
||||
$variation = $product->variations[1];
|
||||
$this->assertEquals('10,90 €', printPrice($variation['price'])); // 12,10 - 10% (product pricelist discount)
|
||||
$variation = $product->variations[2];
|
||||
$this->assertEquals('9,70 €', printPrice($variation['price'])); // 12,10 - 20% (variation pricelist discount)
|
||||
}
|
||||
|
||||
public function testCartCZK()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$pricelistContext->activate(1);
|
||||
|
||||
$this->loginUser(1);
|
||||
|
||||
// insert product id 3, 2 pieces
|
||||
$this->insertProduct(3, null, 2);
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$this->assertEquals(\Decimal::create('566.0000', 4), $this->cart->totalPriceWithVat);
|
||||
|
||||
$this->checkOrderPriceIsSameAsCart();
|
||||
}
|
||||
|
||||
public function testCartVariationCZK()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$pricelistContext->activate(1);
|
||||
|
||||
$this->loginUser(1);
|
||||
|
||||
// insert product id 4, id variation 1
|
||||
$this->insertProduct(4, 1); // price = 315 (no discount)
|
||||
// insert product id 4, id variation 2
|
||||
$this->insertProduct(4, 2); // price = 299 = 315 - 5% (variation pricelist discount)
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$this->assertEquals(\Decimal::create('614.0000', 4), $this->cart->totalPriceWithVat); // 315 + 299
|
||||
|
||||
$this->checkOrderPriceIsSameAsCart();
|
||||
}
|
||||
|
||||
public function testCartEUR()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
|
||||
$this->loginUser(1);
|
||||
|
||||
$currencyContext->activate('EUR');
|
||||
|
||||
// insert product id 3, 2 pieces
|
||||
$this->insertProduct(3, null, 2);
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$this->assertEquals(\Decimal::create('21.8000', 4), $this->cart->totalPriceWithVat);
|
||||
|
||||
$this->checkOrderPriceIsSameAsCart();
|
||||
}
|
||||
|
||||
public function testCartVariationEUR()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
|
||||
$this->loginUser(1);
|
||||
|
||||
$currencyContext->activate('EUR');
|
||||
|
||||
// insert product id 4, id variation 1
|
||||
$this->insertProduct(4, 1); // price = 10,90 € = 12,10 - 10% (product pricelist discount)
|
||||
// insert product id 4, id variation 2
|
||||
$this->insertProduct(4, 2); // price = 9,70 € = 12,10 - 20% (variation pricelist discount)
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$this->assertEquals(\Decimal::create('20.6000', 4), $this->cart->totalPriceWithVat); // 10,90 + 9,70
|
||||
|
||||
$this->checkOrderPriceIsSameAsCart();
|
||||
}
|
||||
|
||||
public function testProductListFetchVariations()
|
||||
{
|
||||
$productList = new \ProductList(false);
|
||||
|
||||
$productList->fetchVariations([11]);
|
||||
|
||||
$product = $productList->getProducts()[1];
|
||||
|
||||
$this->assertEquals(20, $product->discount->asFloat());
|
||||
|
||||
// activate pricelist
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$pricelistContext->activate(1);
|
||||
|
||||
$productList = new \ProductList(false);
|
||||
$productList->fetchVariations(null);
|
||||
|
||||
$product = $productList->getProducts()[1];
|
||||
$this->assertEquals(0, $product->discount->asFloat());
|
||||
|
||||
$product = $productList->getProducts()[4];
|
||||
$this->assertEquals(0, $product->discount->asFloat());
|
||||
$variation = $product->variations[1];
|
||||
$this->assertNull($variation['pricelistDiscount']);
|
||||
$variation = $product->variations[2];
|
||||
$this->assertEquals(5, $variation['pricelistDiscount']);
|
||||
|
||||
$productList = new \ProductList(true); // variationsAsResult
|
||||
$products = $productList->getProducts();
|
||||
$product = $products['4/1'];
|
||||
$this->assertEquals(315, $product->getProductPrice()->getPriceWithVat()->asFloat()); // pricelist price, no discount
|
||||
$this->assertEquals(0, $product->discount->asFloat());
|
||||
$product = $products['4/2'];
|
||||
$this->assertEquals(299, $product->getProductPrice()->getPriceWithVat()->asFloat()); // 315 - 5% (variation pricelist discount)
|
||||
$this->assertEquals(5, $product->discount->asFloat());
|
||||
}
|
||||
|
||||
public function testApplyPriceListCoefficient()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
$currencyContext->activate('EUR');
|
||||
sqlQueryBuilder()->update('pricelists_products')->directValues(['discount' => 25])->andWhere('id_product=6')->execute();
|
||||
|
||||
$this->insertProduct(8); // produkt se zakladni cenou 191.9€
|
||||
$this->insertProduct(7, 6); // varianta se zakladni cenou 48.2€
|
||||
$this->insertProduct(10); // produkt s pevou cenikovou cenou 23€
|
||||
$this->insertProduct(11, 22); // varianta s pevou cenikovou cenou 12.1€*/
|
||||
$this->insertProduct(6, 17); // varianta se slevou 25% ze zakl. ceny 115.4€
|
||||
$this->insertProduct(9); // produkt se slevou 25% ze zakl. ceny 241.9€
|
||||
$this->cart->createFromDB();
|
||||
$expected = 191.9 + 48.2 + 23 + 12.1 + 115.4 * 0.75 + 241.9 * 0.75;
|
||||
self::assertEquals($expected, $this->cart->totalPriceWithVat->asFloat(), 'Porovnani cen pred nastavenim koeficientu ceniku', 0.1);
|
||||
|
||||
/** @var Pricelist $activePricelist */
|
||||
$activePricelist = Contexts::get(PricelistContext::class)->getActive();
|
||||
$activePricelist?->setCoefficient(2);
|
||||
|
||||
$this->cart = $this->get(CartFactory::class)->create();
|
||||
$this->cart->createFromDB();
|
||||
$expected = 191.9 * 2 + 48.2 * 2 + 23 + 12.1 + (115.4 * 2) * 0.75 + (241.9 * 2) * 0.75;
|
||||
self::assertEquals($expected, $this->cart->totalPriceWithVat->asFloat(), 'Porovnani cen po nastaveni koeficientu ceniku', 0.1);
|
||||
}
|
||||
|
||||
public function testApplyPriceListCoefficientProduct()
|
||||
{
|
||||
$pricelistContext = ServiceContainer::getService(PricelistContext::class);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
$priceConverter = ServiceContainer::getService(\KupShop\I18nBundle\Util\PriceConverter::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
$currencyContext = ServiceContainer::getService(CurrencyContext::class);
|
||||
$priceConverter = ServiceContainer::getService(\KupShop\I18nBundle\Util\PriceConverter::class);
|
||||
|
||||
$pricelistContext->activate(2);
|
||||
$currencyContext->activate('EUR');
|
||||
|
||||
$product = new \Product(); // produkt se zakladni cenou 191.9€
|
||||
$product->createFromDB('8');
|
||||
|
||||
self::assertEquals('191,90 €', $product->price, 'Porovnani cen pred nastavenim koeficientu ceniku');
|
||||
self::assertEquals('191.92', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->getProductPrice()->getPriceWithVat())->asFloat(), 2), 'Porovnani cen pred nastavenim koeficientu ceniku');
|
||||
self::assertEquals('191.92', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->priceOriginal->getValue()->addVat(21))->asFloat(), 2), 'Porovnani cen pred nastavenim koeficientu ceniku - price original');
|
||||
self::assertEquals('191.92', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->priceForDiscount->getValue()->addVat(21))->asFloat(), 2), 'Porovnani cen pred nastavenim koeficientu ceniku - price for discount');
|
||||
|
||||
/** @var Pricelist $activePricelist */
|
||||
$activePricelist = Contexts::get(PricelistContext::class)->getActive();
|
||||
$activePricelist?->setCoefficient(2);
|
||||
$product->createFromDB('8');
|
||||
|
||||
self::assertEquals('383,85 €', $product->price, 'Porovnani cen po nastavenim koeficientu ceniku');
|
||||
self::assertEquals('383.85', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->getProductPrice()->getPriceWithVat())->asFloat(), 2), 'Porovnani cen po nastavenim koeficientu ceniku');
|
||||
self::assertEquals('383.85', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->priceOriginal->getValue()->addVat(21))->asFloat(), 2), 'Porovnani cen po nastavenim koeficientu ceniku - price original');
|
||||
self::assertEquals('383.85', round($priceConverter->convert($product->getProductPrice()->getCurrency(), $currencyContext->getActive(), $product->priceForDiscount->getValue()->addVat(21))->asFloat(), 2), 'Porovnani cen po nastavenim koeficientu ceniku - price for discount');
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->getJsonDataSetFromFile();
|
||||
}
|
||||
|
||||
private function insertProduct($id_product, $id_variation = null, $pieces = 1)
|
||||
{
|
||||
$item = [
|
||||
'id_product' => $id_product,
|
||||
'id_variation' => $id_variation,
|
||||
'pieces' => $pieces,
|
||||
];
|
||||
$this->assertGreaterThan(0, $this->cart->addItem($item));
|
||||
}
|
||||
|
||||
private function loginUser($id_user)
|
||||
{
|
||||
$user = \User::createFromId($id_user);
|
||||
$user->activateUser();
|
||||
}
|
||||
|
||||
private function checkOrderPriceIsSameAsCart()
|
||||
{
|
||||
// je to fuj, ale je to kvuli spravnym cenam... aby se zapocitala i cena DeliveryType
|
||||
$this->cart->max_step = 1;
|
||||
$this->cart->createFromDB();
|
||||
|
||||
$order = $this->cart->submitOrder();
|
||||
$this->assertInstanceOf(\Order::class, $order);
|
||||
|
||||
$this->assertEquals($order->total_price->asFloat(), $this->cart->totalPricePay->asFloat(), '', 0.01);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Util\Price;
|
||||
|
||||
use KupShop\KupShopBundle\Util\Price\ProductPrice;
|
||||
|
||||
class PriceListPrice extends ProductPrice
|
||||
{
|
||||
/** @var ProductPrice */
|
||||
protected $originalPrice;
|
||||
|
||||
public function setOriginalPrice(ProductPrice $originalPrice)
|
||||
{
|
||||
$this->originalPrice = $originalPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProductPrice
|
||||
*/
|
||||
public function getOriginalPrice()
|
||||
{
|
||||
return $this->originalPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Decimal
|
||||
*/
|
||||
public function getDiscount()
|
||||
{
|
||||
return $this->discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProductPrice
|
||||
*/
|
||||
public function applyCoefficient(\Decimal $coefficient)
|
||||
{
|
||||
$this->setValue($this->value->mul($coefficient));
|
||||
$this->originalPrice->setValue($this->originalPrice->value->mul($coefficient));
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
264
bundles/KupShop/PricelistBundle/Util/PriceListWorker.php
Normal file
264
bundles/KupShop/PricelistBundle/Util/PriceListWorker.php
Normal file
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\PricelistBundle\Util;
|
||||
|
||||
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
||||
use Query\Operator;
|
||||
|
||||
class PriceListWorker
|
||||
{
|
||||
use \DatabaseCommunication;
|
||||
|
||||
private array $priceListsCache = [];
|
||||
private array $priceListsByNameCache = [];
|
||||
|
||||
public function getPriceList(int $id): ?array
|
||||
{
|
||||
$this->loadPriceListsCache();
|
||||
|
||||
if ($this->priceListsCache[$id] ?? false) {
|
||||
return $this->priceListsCache[$id];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPriceLists(): array
|
||||
{
|
||||
$this->loadPriceListsCache();
|
||||
|
||||
return $this->priceListsCache;
|
||||
}
|
||||
|
||||
public function findPriceList(string $name, string $currency): int
|
||||
{
|
||||
$priceListName = mb_strtolower($name.'-'.$currency);
|
||||
|
||||
if (!$this->priceListsByNameCache) {
|
||||
$this->priceListsByNameCache = Mapping::mapKeys(
|
||||
sqlQueryBuilder()
|
||||
->select('id, name, currency')
|
||||
->from('pricelists')
|
||||
->execute()->fetchAll(),
|
||||
function ($k, $v) {
|
||||
return [mb_strtolower($v['name'].'-'.$v['currency']), (int) $v['id']];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!($this->priceListsByNameCache[$priceListName] ?? false)) {
|
||||
$this->priceListsByNameCache[$priceListName] = sqlGetConnection()->transactional(
|
||||
function () use ($name, $currency) {
|
||||
sqlQueryBuilder()
|
||||
->insert('pricelists')
|
||||
->directValues(
|
||||
[
|
||||
'name' => $name,
|
||||
'currency' => $currency,
|
||||
]
|
||||
)->execute();
|
||||
|
||||
return (int) sqlInsertId();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $this->priceListsByNameCache[$priceListName];
|
||||
}
|
||||
|
||||
public function updatePriceList(int $priceListId, int $productId, ?int $variationId, \Decimal $price, ?\Decimal $discount = null, bool $withDiscountUpdate = true): void
|
||||
{
|
||||
$options = [
|
||||
'price' => $price->asFloat(),
|
||||
'showVat' => 'N',
|
||||
];
|
||||
|
||||
if ($withDiscountUpdate) {
|
||||
$options['discount'] = $discount?->asFloat();
|
||||
}
|
||||
|
||||
$this->updatePricelists(
|
||||
$priceListId,
|
||||
$options,
|
||||
$productId,
|
||||
0,
|
||||
$variationId,
|
||||
$withDiscountUpdate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates discount only (price is not changed).
|
||||
*/
|
||||
public function updatePriceListDiscount(int $priceListId, int $productId, ?int $variationId, ?\Decimal $discount): void
|
||||
{
|
||||
sqlQueryBuilder()
|
||||
->update('pricelists_products')
|
||||
->directValues(['discount' => $discount])
|
||||
->where(Operator::equals(['id' => $this->getPriceListItemId($priceListId, $productId, $variationId)]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/** @deprecated use updatePriceList */
|
||||
public function updatePricelists(int $pricelistId, array $options, int $productId, ?float $vat, ?int $variationId = null, bool $withDiscountUpdate = true)
|
||||
{
|
||||
if (empty($options['price'])) {
|
||||
$options['price'] = null;
|
||||
}
|
||||
if (empty($options['discount'])) {
|
||||
$options['discount'] = null;
|
||||
}
|
||||
|
||||
$data = ['id_pricelist' => $pricelistId, 'id_product' => $productId, 'id_variation' => $variationId];
|
||||
|
||||
$pricelistProduct = sqlQueryBuilder()
|
||||
->select('*')
|
||||
->from('pricelists_products')
|
||||
->where(Operator::equalsNullable($data))
|
||||
->execute()->fetchAssociative();
|
||||
|
||||
if (is_null($options['price']) && is_null($options['discount'])) {
|
||||
if (!empty($pricelistProduct)) {
|
||||
if (!$withDiscountUpdate) {
|
||||
$affectedRows = sqlQueryBuilder()
|
||||
->delete('pricelists_products')
|
||||
->where(Operator::equals(['id' => $pricelistProduct['id']]))
|
||||
->andWhere(Operator::isNull('discount'))
|
||||
->execute();
|
||||
|
||||
if (!$affectedRows) {
|
||||
sqlQueryBuilder()
|
||||
->update('pricelists_products')
|
||||
->directValues(['price' => null])
|
||||
->andWhere(Operator::equals(['id' => $pricelistProduct['id']]))
|
||||
->andWhere(Operator::not(Operator::isNull('discount')))
|
||||
->execute();
|
||||
}
|
||||
} else {
|
||||
sqlQueryBuilder()->delete('pricelists_products')
|
||||
->where(Operator::equals(['id' => $pricelistProduct['id']]))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($pricelistProduct)) {
|
||||
$pricelistProduct = $data;
|
||||
}
|
||||
|
||||
$price = $this->preparePrice($options['price']);
|
||||
// vat remove before saving to database
|
||||
if ($options['showVat'] == 'Y') {
|
||||
$price = $this->removeVatFromPrice($price, $vat);
|
||||
}
|
||||
|
||||
$discount = null;
|
||||
if (!$withDiscountUpdate) {
|
||||
$discount = $this->getPriceListDiscount($pricelistId, $productId, $variationId);
|
||||
}
|
||||
|
||||
if ($discount || $withDiscountUpdate) {
|
||||
$pricelistProduct['discount'] = $options['discount'] ?? $discount;
|
||||
}
|
||||
|
||||
$pricelistProduct['price'] = $price;
|
||||
$pricelistProduct['generated'] = !isset($options['generated']) ? $pricelistProduct['generated'] ?? 0 : $options['generated'];
|
||||
|
||||
sqlQueryBuilder()
|
||||
->insert('pricelists_products')
|
||||
->directValues($pricelistProduct)
|
||||
->onDuplicateKeyUpdate(['price', 'discount', 'generated'])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Decimal
|
||||
*/
|
||||
public function removeVatFromPrice($price, $vat)
|
||||
{
|
||||
if ($price == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$price = \Decimal::create($price, 4);
|
||||
|
||||
return $price->mul(\Decimal::create(100))->div(\Decimal::create(100 + $vat));
|
||||
}
|
||||
|
||||
public function getPriceListItemId(int $priceListId, int $productId, ?int $variationId = null): int
|
||||
{
|
||||
$id = sqlQueryBuilder()
|
||||
->select('id')
|
||||
->from('pricelists_products')
|
||||
->where(
|
||||
Operator::equalsNullable(
|
||||
[
|
||||
'id_pricelist' => $priceListId,
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
])
|
||||
)->execute()->fetchOne();
|
||||
|
||||
if (!$id) {
|
||||
$id = sqlGetConnection()->transactional(function () use ($priceListId, $productId, $variationId) {
|
||||
sqlQueryBuilder()
|
||||
->insert('pricelists_products')
|
||||
->directValues(
|
||||
[
|
||||
'id_pricelist' => $priceListId,
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
]
|
||||
)->execute();
|
||||
|
||||
return (int) sqlInsertId();
|
||||
});
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function getPriceListItem(int $priceListId, int $productId, ?int $variationId = null): ?array
|
||||
{
|
||||
$item = sqlQueryBuilder()
|
||||
->select('*')
|
||||
->from('pricelists_products')
|
||||
->where(
|
||||
Operator::equalsNullable(
|
||||
[
|
||||
'id_pricelist' => $priceListId,
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
])
|
||||
)->execute()->fetchAssociative();
|
||||
|
||||
return $item ?: null;
|
||||
}
|
||||
|
||||
protected function loadPriceListsCache(): void
|
||||
{
|
||||
if (!$this->priceListsCache) {
|
||||
$this->priceListsCache = Mapping::mapKeys(
|
||||
sqlQueryBuilder()
|
||||
->select('id, name, currency')
|
||||
->from('pricelists')
|
||||
->execute()->fetchAllAssociative(),
|
||||
function ($k, $v) {
|
||||
return [(int) $v['id'], $v];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPriceListDiscount(int $priceListId, int $productId, ?int $variationId)
|
||||
{
|
||||
return sqlQueryBuilder()
|
||||
->select('discount')
|
||||
->from('pricelists_products')
|
||||
->where(Operator::equalsNullable(['id_pricelist' => $priceListId, 'id_product' => $productId, 'id_variation' => $variationId]))
|
||||
->execute()
|
||||
->fetchOne();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user