first commit

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

View File

@@ -0,0 +1,82 @@
<?php
namespace KupShop\ShoppingListBundle\Admin;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\ShoppingListBundle\Util\ShoppingListUtil;
use Query\Operator;
class ShoppingList extends \Window
{
protected $tableName = 'shopping_list';
/** @var ShoppingListUtil */
public $shoppingListUtil;
public function __construct()
{
$this->shoppingListUtil = ServiceContainer::getService(ShoppingListUtil::class);
}
public function get_vars()
{
$vars = parent::get_vars();
if ($ID = $this->getID()) {
$sl = $this->shoppingListUtil->getShoppingList($ID);
$vars['items'] = $this->shoppingListUtil->fetchItems($ID, true)->getProducts();
$vars['name'] = $sl->getName();
$vars['id_user'] = $sl->getIdUser();
$vars['note'] = $sl->getNote();
$vars['date'] = $sl->getDate() ?: date('Y-m-d');
}
return $vars;
}
public function handleUpdate()
{
$SQL = parent::handleUpdate(); // TODO: Change the autogenerated stub
$data = $this->getData();
$id = $this->getID();
$acn = $this->getAction();
if ($SQL && !empty($data)) {
if ($acn == 'add') {
$this->shoppingListUtil->updateHash($id, date('Y-m-d'));
$this->processShoppingListItems($id, $data['items'], true);
$this->returnOK('Nákupní seznam byl vytvořen');
} elseif ($acn == 'edit') {
$this->processShoppingListItems($id, $data['items']);
$this->returnOK('Nákupní seznam byl upraven');
}
}
return $SQL;
}
protected function processShoppingListItems($id_shopping_list, $items, $duplicate = false): void
{
if (is_iterable($items)) {
foreach ($items as $id => $item) {
if ($id == 0) {
continue;
}
if ($id > 0 && isset($item['delete'])) {
$this->shoppingListUtil->removeProductFromList($item['id']);
} elseif ($id > 0 && !$duplicate) {
sqlQueryBuilder()->update('shopping_list_products')
->set('pieces', $item['pieces'])
->where(Operator::equals(['id' => $item['id']]))->execute();
} elseif (!empty($item['id_product'])) {
$this->shoppingListUtil->addProductToShoppingList(
(int) $id_shopping_list,
(int) $item['id_product'],
(int) $item['id_variation'] ?: null,
floatval($item['pieces']));
}
}
}
}
}
return ShoppingList::class;

View File

@@ -0,0 +1,16 @@
<?php
$txt_str['ShoppingList'] = [
'toolbar_list' => 'Seznam nákupních seznamů',
'toolbar_add' => 'Přidat seznam',
'noUserSelected' => 'Uživatel',
'description' => 'Popis',
'flapList' => 'Nákupní seznam',
'name' => 'Název',
'submitAddNewItem' => 'Přidat další produkt',
'editItems' => 'Uložit',
'titleEdit' => 'Úprava nákupního seznamu',
];

View File

@@ -0,0 +1,55 @@
<?php
namespace KupShop\ShoppingListBundle\Admin\lists;
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
class ShoppingListList extends BaseList
{
protected $tableDef = [
'id' => 'id',
'fields' => [
'Uživatel' => ['field' => 'id_user', 'size' => 0.6, 'render' => 'renderCustomer'],
'Název' => ['field' => 'name', 'size' => 2],
'Datum' => ['field' => 'date', 'size' => 1.5, 'render' => 'renderDate'],
'Poznámka' => ['field' => 'note', 'size' => 0.6],
],
];
public function getQuery()
{
return sqlQueryBuilder()
->select('s.*, u.name as user_name, u.surname as user_surname, u.firm')
->from('shopping_list', 's')
->leftJoin('s', 'users', 'u', 'u.id = s.id_user');
}
public function renderCustomer($values)
{
$userID = $values['id_user'];
if (!$userID) {
return $values['id_user'];
}
$parts = [];
$name = $this->getListRowValue($values, 'user_name');
$surname = $this->getListRowValue($values, 'user_surname');
$company = $this->getListRowValue($values, 'firm');
if ($company) {
$parts[] = $company;
}
if ($name || $surname) {
$parts[] = implode(' ', [$surname, $name]);
}
return HTML::create('a')
->attr('href', 'javascript:nw(\'users\', \''.$values['id_user'].'\')')
->text(implode(', ', $parts));
}
}
return ShoppingListList::class;

View File

@@ -0,0 +1,3 @@
<p>{ldelim}OD{rdelim} Vám posílá nákupní seznam: </p>
{ldelim}PRODUKTY_V_SEZNAMU{rdelim}

View File

@@ -0,0 +1,181 @@
{extends "[shared]window.tpl"}
{block tabs}
{windowTab id='flapList'}
{/block}
{block tabsContent}
<div id="flapList" class="tab-pane fade active in boxStatic">
<input type="hidden" name="data[date]" value="{$date}"/>
<div class="row">
<div class="col-md-6 col-xs-12">
<div class="wpj-form-group">
<label for="data[name]">{'name'|translate}</label>
<input type="text" id="data[name]" name="data[name]" class="form-control" value="{$name}"/>
</div>
</div>
<div class="col-md-6 col-xs-12">
<label for="data[id_user]">{'noUserSelected'|translate}</label>
<select id="data[id_user]" name="data[id_user]" class="selecter" data-autocomplete="users" data-preload="users">
{if !$id_user}
<option value="">-- bez uživatele --</option>
{else}
<option value="{$id_user}">{$id_user}</option>
{/if}
</select>
</div>
<div class="col-xs-12">
<div class="wpj-form-group">
<label for="data[note]">{'description'|translate}</label>
<textarea id="data[note]" name="data[note]" class="form-control">{$note}</textarea>
</div>
</div>
</div>
<div id="items">
<div class="row">
<div class="col-xs-3">
<div class="wpj-form-group">
<a href="#" data-form-add class="btn btn-success btn-block"><span
class="bi bi-plus-lg m-r-1"></span>&nbsp;{'submitAddNewItem'|translate nofilter}
</a>
</div>
</div>
</div>
<div class="wpj-panel wpj-panel-default m-b-1" data-form-new style="display:none">
<div class="wpj-panel-body">
{block 'autocomplete-html'}
<div class="row wpj-form-group-flex">
<div class="col-md-8 col-xs-12">
<div class="wpj-form-group">
<label for="data[product]">{'productItem'|translate:'orders'}</label>
<input type="text" data-autocomplete-search="product" autocomplete="off" id="data[product]" name="data[product]"
class="form-control autocomplete-control" placeholder="Vyhledat produkt"
data-preload="product_variation"
value="{if $body.data.id_product}{$body.data.id_product}{if $body.data.id_variation}-{$body.data.id_variation}{/if}{/if}">
<input type="hidden" name="data[items][0][id_product]" value="{$body.data.id_product}">
<input type="hidden" name="data[items][0][id_variation]" value="{$body.data.id_variation}">
<script type="application/javascript">
$('[data-autocomplete-search="product"]').adminVariationAutoComplete({
allowSelectProduct: false,
select: function (e, item) {
let { product, variation } = item;
if (variation && typeof variation !== 'string' && variation.id) {
variation = variation.id
}
$('[name="data[items][0][id_product]"]').val(product.id);
$('[name="data[items][0][id_variation]"]').val(variation);
return true;
}
});
</script>
</div>
</div>
<div class="col-md-3 col-xs-10">
<div class="wpj-form-group">
<label for="data[items][0][pieces]">{'countPieces'|translate:'orders'}</label>
<div class="input-group">
<input type="text" class="form-control input-sm" id="data[items][0][pieces]" name="data[items][0][pieces]"
size="5" maxlength="11"
data-calculate='pieces' value="1">
<span class="input-group-addon">{'piecesUnit'|translate:'orders'}</span>
</div>
</div>
</div>
<div class="col-md-1 col-xs-2 text-right">
<div class="wpj-form-group">
<a class="btn btn-danger" data-form-delete>
<input class="hidden" type="checkbox" name="data[items][0][delete]">
<span class="bi bi-trash"></span>
</a>
</div>
</div>
</div>
{/block}
</div>
</div>
<div class="wpj-panel wpj-panel-default">
<div class="wpj-panel-heading">
<div class="row">
<div class="col-xs-7">
<p><strong>{'productItem'|translate:'orders'}</strong></p>
</div>
<div class="col-xs-2">
<p><strong>{'pieces'|translate:'orders'}</strong></p>
</div>
<div class="col-xs-2">
<p><strong>{'piecePrice'|translate:'orders'} {$labelPricePreferred}</strong></p>
</div>
<div class="col-xs-1">
</div>
</div>
</div>
<div class="wpj-list-group">
{block "order-items"}
{foreach $items as $item}
<div class="wpj-list-group-item" data-form-item="{$item.id}">
<input type="hidden" name="data[items][{$item.id}][id_product]" value="{$item.id}">
<input type="hidden" name="data[items][{$item.id}][id_variation]" value="{$item.variationId}">
<input type="hidden" name="data[items][{$item.id}][id]" value="{$item.id_slp}">
<div class="row">
<div class="col-xs-7">
<strong>{$item.title}</strong>
{if $item.variationId}
<br><small class="variation-title">{$item.variationTitle}</small>
{/if}
</div>
<div class="col-xs-2">
<input class="form-control" type="number" name="data[items][{$item.id}][pieces]"
value="{$item.pieces}">
</div>
<div class="col-xs-2">
{$item.price_array|format_price}
</div>
<div class="col-xs-1 text-right">
<a class="btn btn-danger" data-form-delete>
<input class="hidden" type="checkbox" name="data[items][{$item.id}][delete]">
<span class="bi bi-trash"></span>
</a>
</div>
</div>
</div>
{/foreach}
{/block}
</div>
</div>
</div>
</div>
{/block}
<script type="text/javascript">
{block onready append}
initForm({
selector: '#items',
beforeAdd: function (original) {
var $form = original();
createAutocomplete($form);
}
});
function createAutocomplete($form) {
$form.find('[data-autocomplete-search="product"]').adminVariationAutoComplete({
select: function (e, item) {
$form.find(":input[name*=id_product]").val(item.product.id);
if (item.variation) {
$form.find(":input[name*=id_variation]").val(item.variation.id);
}
}
});
}
{/block}
</script>

View File

@@ -0,0 +1,28 @@
<?php
namespace KupShop\ShoppingListBundle\AdminRegister;
use KupShop\AdminBundle\AdminRegister\AdminRegister;
use KupShop\AdminBundle\AdminRegister\IAdminRegisterDynamic;
use KupShop\AdminBundle\AdminRegister\IAdminRegisterStatic;
class ShoppingListAdminRegister extends AdminRegister implements IAdminRegisterStatic, IAdminRegisterDynamic
{
public function getDynamicMenu(): array
{
return [
static::createMenuItem('ordersMenu', [
'name' => 'Shoppinglist',
'left' => 's=menu.php&type=ShoppingList',
'right' => 's=list.php&type=ShoppingList',
]),
];
}
public static function getPermissions(): array
{
return [
static::createPermissions('Shoppinglist', [\Modules::SHOPPING_LIST], ['SHOPPINGLIST']),
];
}
}

View File

@@ -0,0 +1,253 @@
<?php
namespace KupShop\ShoppingListBundle\Controller;
use KupShop\GraphQLBundle\EventListener\JsShopRefreshListener;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Routing\TranslatedRoute;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\ShoppingListBundle\Util\ShoppingListUtil;
use KupShop\ShoppingListBundle\View\ShoppingListCreateView;
use KupShop\ShoppingListBundle\View\ShoppingListProductsView;
use KupShop\ShoppingListBundle\View\ShoppingListSharedView;
use KupShop\ShoppingListBundle\View\ShoppingListView;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ShoppingListController extends AbstractController
{
public function __construct(protected readonly ShoppingListUtil $shoppingListUtil)
{
}
/**
* @TranslatedRoute("/#shopping-list#")
*/
public function shoppingListAction(Request $request, ShoppingListView $view)
{
if (!\User::getCurrentUser()) {
addUserMessage(translate('need_log_in', 'shopping_list'), 'danger');
return new RedirectResponse(
path('kupshop_user_login_login', ['url' => urlencode($request->getUri())])
);
}
return $view->getResponse($request);
}
/**
* @TranslatedRoute("/#shopping-list#/odebrat/{id}")
*/
public function removeAction($id)
{
if (!$this->shoppingListUtil->isUserOwner($id)) {
throw new NotFoundHttpException(translate('delete_permissions', 'shopping_list'));
}
$this->shoppingListUtil->deleteShoppingList($id);
addUserMessage(translate('list_delete', 'shopping_list'), 'success');
return new RedirectResponse(path('kupshop_shoppinglist_shoppinglist_shoppinglist'));
}
/**
* @TranslatedRoute("/#shopping-list#/odebrat-produkt/{id}/")
*/
public function removeProductAction(Request $request, $id)
{
$idSl = $this->shoppingListUtil->getProductlistIdForProduct($id);
if (!$this->shoppingListUtil->isUserOwner($idSl)) {
throw new NotFoundHttpException(translate('delete_permissions', 'shopping_list'));
}
$this->shoppingListUtil->removeProductFromList($id);
addUserMessage(translate('list_product_delete', 'shopping_list'), 'success');
if (findModule(\Modules::JS_SHOP) && $this->shoppingListUtil->getShoppingListLabel($idSl) === 'favorites') {
$request->getSession()->set(JsShopRefreshListener::SESSION_NAME, true);
}
return new RedirectResponse($this->getNextUrl($request));
}
/**
* @TranslatedRoute("/#shopping-list#/vytvorit/")
*
* @return RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function createShoppingList(Request $request, ShoppingListCreateView $view)
{
if ($request->request->get('newListName')) {
$listId = $this->shoppingListUtil->createShoppingList($request->request->get('newListName'));
return new RedirectResponse(
path('kupshop_shoppinglist_shoppinglist_showshoppinglistproducts', ['listId' => $listId])
);
}
addUserMessage(translate('enter_name', 'shopping_list'), 'danger');
return $view->getResponse($request);
}
/**
* @TranslatedRoute("/#shopping-list#/{listId}/", requirements={"listId":"[0-9]+"})
*/
public function showShoppingListProducts(ShoppingListProductsView $view, $listId, Request $request)
{
if (!$this->shoppingListUtil->isUserOwner($listId)) {
throw new NotFoundHttpException(translate('not_permission', 'shopping_list'));
}
$view->setIdList($listId);
return $view->getResponse($request);
}
/**
* @TranslatedRoute("/#shopping-list#/{listId}/{hash}", requirements={"listId":"[0-9]+", "hash":".*"})
*/
public function showSharedShoppingListProducts(ShoppingListSharedView $view, $listId, $hash, Request $request)
{
if (!($this->shoppingListUtil->validHash($listId, $hash) || $this->shoppingListUtil->isUserOwner($listId))) {
addUserMessage(translate('not_permission', 'shopping_list'), 'danger');
throw new NotFoundHttpException('Shopping list not found');
}
$view->setIdList($listId);
return $view->getResponse($request);
}
/**
* @TranslatedRoute("/#shopping-list#/pridat/")
*/
public function addProductToShoppingList(Request $request)
{
$listId = $request->get('shoppingListId');
if (!$listId && $request->get('newListName')) {
$listId = $this->shoppingListUtil->createShoppingList($request->request->get('newListName'));
} elseif (!$listId) {
addUserMessage(translate('select_shopping_list', 'shopping_list'), 'danger');
return new RedirectResponse(
path('kupshop_shoppinglist_shoppinglist_shoppinglist')
);
}
if (!$this->shoppingListUtil->isUserOwner($listId)) {
addUserMessage(translate('not_permission', 'shopping_list'), 'danger');
return new RedirectResponse(
path('kupshop_shoppinglist_shoppinglist_shoppinglist')
);
}
$this->shoppingListUtil->addToShoppingList($listId, $request->get('products') ?? []);
addUserMessage(translate('add_to_shopping_list', 'shopping_list'), 'success');
if ($request->get('addFromCart')) {
addUserMessage(translate('copy_to_shopping_list', 'shopping_list'), 'success');
$this->shoppingListUtil->addItemsFromCart($listId);
}
return new RedirectResponse($this->getNextUrl($request));
}
/**
* @TranslatedRoute("/#shopping-list#/do-kosiku/{listId}", requirements={"listId":"[0-9]+"})
*/
public function addEntireListToCart($listId, ShoppingListUtil $shoppingListUtil)
{
foreach ($this->shoppingListUtil->fetchItems($listId)->getProducts() as $item) {
$cartItem = [
'id_product' => $item->id,
'id_variation' => $item->variationId ?? null,
'pieces' => $item['pieces'],
];
$shoppingListUtil->addItemToCart($cartItem);
}
addUserMessage(translate('added_items_to_cart', 'shopping_list'), 'success');
return new RedirectResponse(path('kupshop_shoppinglist_shoppinglist_shoppinglist'));
}
/**
* @TranslatedRoute("/#shopping-list#/do-kosiku/")
*/
public function addShoppingListToCart(Request $request)
{
$slProducts = $request->get('products');
if ($request->get('updateCount') && is_iterable($slProducts)) {
$this->shoppingListUtil->updateShoppingList($slProducts);
addUserMessage(translate('update_shopping_list', 'shopping_list'), 'success');
} elseif ($request->get('addProduct')) {
$pieces = $slProducts[$request->get('addProduct')] ?? null;
$this->shoppingListUtil->addItemsToCart([intval($request->get('addProduct')) => $pieces]);
addUserMessage(translate('added_items_to_cart', 'shopping_list'), 'success');
} elseif ($request->get('addAllProducts') && is_iterable($slProducts)) {
$this->shoppingListUtil->addItemsToCart($slProducts);
addUserMessage(translate('added_items_to_cart', 'shopping_list'), 'success');
}
return new RedirectResponse($this->getNextUrl($request));
}
#[TranslatedRoute(path: '/#shopping-list#/prejmenovat/{listId}')]
public function renameShoppingList(Request $request, $listId)
{
if (!$this->shoppingListUtil->isUserOwner($listId)) {
throw new NotFoundHttpException('Shopping list not found');
}
$name = $request->get('name');
$from = $request->get('from');
if (!empty($name)) {
$this->shoppingListUtil->renameShoppingList($listId, $name);
}
if ($from == 'detail') {
return new RedirectResponse(path('kupshop_shoppinglist_shoppinglist_showshoppinglistproducts', ['listId' => $listId]));
} else {
return new RedirectResponse(path('kupshop_shoppinglist_shoppinglist_shoppinglist'));
}
}
#[TranslatedRoute(path: '/#shopping-list#/sdilet/')]
public function shareShoppingList(Request $request): RedirectResponse
{
$email = $request->get('email');
$listId = $request->get('listId');
if (!$nameFrom = $request->get('nameFrom')) {
$userContext = Contexts::get(UserContext::class);
$user = $userContext->getActive();
$nameFrom = $user->name.' '.$user->surname ?? '';
}
if (!$this->shoppingListUtil->isUserOwner($listId)) {
throw new NotFoundHttpException('Shopping list not found');
}
$this->shoppingListUtil->shareShoppingList($listId, $email, $nameFrom);
addUserMessage(translate('shopping_list_email_sent', 'shopping_list'), 'success');
return new RedirectResponse(path('kupshop_shoppinglist_shoppinglist_shoppinglist'));
}
private function getNextUrl(Request $request)
{
if ($next = $request->query->get('NEXT')) {
return $next;
}
return $request->headers->get('referer', '/');
}
}

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace KupShop\ShoppingListBundle\Email;
use KupShop\KupShopBundle\Email\BaseEmail;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\ShoppingListBundle\Util\ShoppingListUtil;
use Symfony\Contracts\Service\Attribute\Required;
class ShareShoppingListEmail extends BaseEmail
{
protected static $name = 'Sdílení nákupního seznamu';
protected static $type = 'SHARE_SHOPPING_LIST';
protected static $priority = -20;
protected $subject = 'Můj nákupní seznam';
protected $template = 'email/email_share_shopping_list.tpl';
public ShoppingListUtil $shoppingListUtil;
public int $listId;
public string $from;
#[Required]
final public function setShoppingListUtil(ShoppingListUtil $shoppingListUtil): void
{
$this->shoppingListUtil = $shoppingListUtil;
}
public function setListId(int $listId): void
{
$this->listId = $listId;
}
public function setFrom(string $from): void
{
$this->from = $from;
}
public function testEmail(): array
{
$listId = sqlQueryBuilder()
->select('id')
->from('shopping_list')
->setMaxResults(1)
->execute()->fetchOne();
$shoppingList = $this->shoppingListUtil->getShoppingList($listId);
$this->setListId($shoppingList->getId());
$this->setFrom('Testovací Uživatel');
return parent::testEmail();
}
public function __wakeup()
{
parent::__wakeup();
$this->shoppingListUtil = ServiceContainer::getService(ShoppingListUtil::class);
}
public function __sleep()
{
return array_diff(parent::__sleep(), ['shoppingListUtil']);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace KupShop\ShoppingListBundle\EmailRegister;
use KupShop\KupShopBundle\Email\EmailInterface;
use KupShop\KupShopBundle\EmailRegister\EmailRegisterInterface;
use KupShop\ShoppingListBundle\Email\ShareShoppingListEmail;
class ShoppingListEmailRegister implements EmailRegisterInterface
{
public function applyPlaceholders(EmailInterface $email): void
{
if ($email instanceof ShareShoppingListEmail) {
$email->addPlaceholder('PRODUKTY_V_SEZNAMU', function () use ($email) {
$smarty = createSmarty(false, true);
$smarty->assign('products', $email->shoppingListUtil->fetchItems($email->listId)->getProducts());
return $smarty->fetch('email/block.shopping-list-items.tpl');
}, 'Produkty v nákupním seznamu');
$email->addPlaceholder('OD', function () use ($email) {
return $email->from;
}, 'Nákupní seznam uživatele');
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace KupShop\ShoppingListBundle\Entity;
class ShoppingListEntity
{
private $id;
private $id_user;
private $name;
private $date;
private $note;
public $hash;
private $label;
public function __construct($id = null)
{
$this->id = $id;
}
public function getId(): int
{
return (int) $this->id;
}
public function getIdUser()
{
return $this->id_user;
}
public function getName()
{
return $this->name;
}
public function getNote()
{
return $this->note;
}
public function setIdUser($id_user): void
{
$this->id_user = $id_user;
}
public function setName($name): void
{
$this->name = $name;
}
public function setNote($note): void
{
$this->note = $note;
}
public function setId(?int $id): ShoppingListEntity
{
$this->id = $id;
return $this;
}
public function setDate($date): void
{
$this->date = $date;
}
public function getDate()
{
return $this->date;
}
public function getHash()
{
return $this->hash;
}
public function setHash($hash): void
{
$this->hash = $hash;
}
public function getLabel()
{
return $this->label;
}
public function setLabel($label): void
{
$this->label = $label;
}
}

View File

@@ -0,0 +1,3 @@
ProductSetsBundle:
resource: "@ShoppingListBundle/Controller"
type: annotation

View File

@@ -0,0 +1,7 @@
services:
_defaults:
autowire: true
autoconfigure: true
KupShop\ShoppingListBundle\:
resource: ../../{Controller,View,AdminRegister,Entity,Util,Email,EmailRegister}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Smarty plugin.
*/
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Util\Contexts;
/**
* Type: function<br>
* Name: get_shopping_lists<br>
* Purpose: return array of shopping lists.
*
* @param array $params parameters
* @param Smarty_Internal_Template $smarty template object
*
* @return string
*/
function smarty_function_get_shopping_lists($params, &$smarty)
{
$userContext = Contexts::get(UserContext::class);
if ($userContext->getActiveId()) {
$userId = (int) $userContext->getActiveId();
} else {
return null;
}
$SQL = sqlQueryBuilder()
->select('id, name')
->from('shopping_list')
->andWhere(\Query\Operator::equals(['id_user' => $userId]))
->execute();
if (!empty($SQL)) {
foreach ($SQL as $list) {
$lists[] = ['id' => $list['id'], 'name' => $list['name']];
}
}
if (!empty($params['assign']) && !empty($lists)) {
$smarty->assign($params['assign'], $lists);
} else {
return null;
}
}

View File

@@ -0,0 +1,3 @@
{foreach $products as $product}
<a href="{url s='redir' type='product' id=$product.id absolute="1"}">{$product.title}</a><br />
{/foreach}

View File

@@ -0,0 +1,76 @@
{extends "focus/focus.base.tpl"}
{block "focus-title"}
{if $onlyCreate}
{t}Vytvořit nový seznam{/t}
{else}
{t}Přidat do seznamu{/t}
{/if}
{/block}
{block "focus-content"}
{if $body.product.variations.variations|count and !$product.variationId}
<div class="alert alert-danger">{t}Nejprve vyberte variantu produktu.{/t}</div>
{elseif $onlyCreate}
<form action="{path('kupshop_shoppinglist_shoppinglist_createshoppinglist')}" method="post" data-shopping-list-form>
<div class="form-group">
<input type="text" name="newListName" class="form-control" placeholder="{t}Název seznamu{/t}">
</div>
<button class="btn btn-primary btn-block" type="submit" disabled>{t}Vytvořit{/t}</button>
</form>
{else}
<form action="{path('kupshop_shoppinglist_shoppinglist_addproducttoshoppinglist')}" method="post" data-shopping-list-form>
{get_shopping_lists assign='shoppingLists'}
{if $shoppingLists|count}
<div class="form-group">
<label>{t}Nákupní seznam{/t}</label>
<select name="shoppingListId" class="form-control input-sm">
<option value="">{t}Nevybráno{/t}</option>
{foreach $shoppingLists as $value}
<option value="{$value.id}">
{$value.name}
</option>
{/foreach}
</select>
</div>
<p>{t}nebo{/t}</p>
{/if}
<div class="form-group">
<input type="text" name="newListName" class="form-control input-sm" placeholder="{t}Vytvořit nový seznam{/t}">
</div>
{if $cart}
<input type="hidden" name="addFromCart" value="1">
{else}
{if $product.variationId}
<input type="hidden" name="products[{$product.id}][variationId]" value="{$product.variationId}">
{/if}
<input type="hidden" name="products[{$body.product.id|default:$product.id}][productId]"
value="{$body.product.id|default:$product.id}">
<input type="hidden" name="products[{$body.product.id|default:$product.id}][pieces]" value="1">
{/if}
<button class="btn btn-primary btn-block" type="submit" disabled>{t}Uložit{/t}</button>
</form>
{/if}
{if !($body.product.variations.variations|count and !$product.variationId)}
<script>
wpj.onReady.push(function () {
const $selectList = $('[name="shoppingListId"]');
const $newList = $('[name="newListName"]');
const $submitBtn = $('[data-shopping-list-form] button[type="submit"]');
const toggleSubmitBtn = () => {
const isEnabled = !!$selectList.val() || !!$newList.val();
$submitBtn.attr('disabled', () => isEnabled ? null : 'disabled');
};
toggleSubmitBtn();
$newList.on('input', toggleSubmitBtn);
if ($selectList.length > 0) {
$selectList.on('change', toggleSubmitBtn);
}
});
</script>
{/if}
{/block}

View File

@@ -0,0 +1,19 @@
{extends "focus/focus.base.tpl"}
{block "focus-title"}
{t}Přejmenovat{/t}
{/block}
{block "focus-content"}
<form method="post"
action="{path("kupshop_shoppinglist_shoppinglist_renameshoppinglist", ['listId' => $body.listId, 'from' => $from|default: 'detail'])}">
<div class="form-group">
<label for="list-name">{t}Nový název nákupního seznamu{/t}</label>
<input id="list-name" name="name" value="{$body.listName}" class="form-control" data-copy="val">
</div>
<div class="shopping-list-buttons">
<button data-listid="{$listId}" class="btn btn-primary" data-focus="close">{t}Zrušit{/t}</button>
<input type="submit" class="btn btn-ctr" value="{t}Přejmenovat{/t}"/>
</div>
</form>
{/block}

View File

@@ -0,0 +1,26 @@
{extends "focus/focus.base.tpl"}
{block "focus-title"}
{t}Sdílet seznam{/t}
{/block}
{block "focus-content"}
<div class="form-group">
<label>{t}Odkaz na seznam{/t} {$body.listName|default:"{t}bez názvu{/t}"}</label>
<input name="share"
value="{path('kupshop_shoppinglist_shoppinglist_showsharedshoppinglistproducts', ['listId'=>$body.listId, 'hash'=>$body.listHash], 0)}"
class="form-control" readonly data-copy="val">
<button class="btn btn-primary btn-block" data-copy="btn">{t}Zkopírovat odkaz{/t}</button>
</div>
<div class="form-group">
<form action="{path('kupshop_shoppinglist_shoppinglist_shareshoppinglist')}">
<input type="hidden" value="{$body.listId}" name="listId">
<label>{t}Poslat e-mailem seznam{/t} {$body.listName|default:"{t}bez názvu{/t}"}</label>
<div class="form-group"><input class="form-control" type="email" name="email" placeholder="{t}E-mail{/t}"></div>
<div class="form-group"><input class="form-control" type="text" name="nameFrom" placeholder="{t}Jméno{/t}"></div>
<input class="btn btn-primary" type="submit" value="{t}Odeslat e-mail{/t}">
</form>
</div>
{/block}

View File

@@ -0,0 +1,15 @@
{extends "account/account-wrapper.tpl"}
{block "title"}
<h1>{$view->getTitle()}</h1>
{/block}
{block "account-content"}
<form method="post" action="{path('kupshop_shoppinglist_shoppinglist_createshoppinglist')}">
<div class="form-group">
<label for="newListName">{t}Název{/t}</label>
<input type="text" name="newListName" id="newListName" class="form-control">
</div>
<button name="submit" type="submit" value="{t}Vytvořit{/t}" class="btn btn-search" >{t}Vytvořit{/t}</button>
</form>
{/block}

View File

@@ -0,0 +1,68 @@
{extends "account/account-wrapper.tpl"}
{block "title"}
<h1>{$view->getTitle()}</h1>
{/block}
{block "account-content"}
<table class="table table-shopping-lists">
<thead>
<tr>
<th>{t}Název{/t}</th>
<th>{t}Počet položek{/t}</th>
<th>{t}Datum vytvoření{/t}</th>
<th></th>
</tr>
</thead>
<tbody>
{foreach $body.lists as $list}
<tr>
<td>
<a href="{path('kupshop_shoppinglist_shoppinglist_showshoppinglistproducts', ['listId'=>$list.id])}">
{$list.name|default:"{t}bez názvu{/t}"}
</a>
</td>
<td>{$list.pocet}</td>
<td>{$list.date|date_format:"%d. %m. %Y"}</td>
<td class="link">
{*<a href="{path('kupshop_shoppinglist_shoppinglist_addentirelisttocart', ['listId'=>$list.id])}">
<span class="fc icons_cart"></span>
</a>*}
<a data-wpj-focus="{path('kupshop_content_focus_focuslazy', ['template' => 'shopping-list-share', 'listId'=>$list.id, 'listHash'=>$list.hash, 'listName' => $list.name|default:"{t}bez názvu{/t}"])}" href="">
<span class="fc icons_share"></span>
</a>
{if $list.label == 'favorites'}
<a disabled style="pointer-events: none; opacity: .2;">
{else}
<a data-wpj-focus="{path("kupshop_content_focus_focuslazy", ['template' => 'shopping-list-rename','listId' => $list.id, 'listName' => $list.name|default:"{t}bez názvu{/t}"])}"
href="" data-wpj-focus-remove-on-close="1">
{/if}
<span class="fc icons_edit"></span>
</a>
{if $list.label == 'favorites'}
<a disabled style="pointer-events: none; opacity: .2;">
{else}
<a href="{path('kupshop_shoppinglist_shoppinglist_remove', ['id'=>$list.id])}"
data-list-delete="{$list.name|default:"{t}bez názvu{/t}"}"
data-info-message="{t}Opravdu chcete smazat seznam{/t}">
{/if}
<span class="fc icons_trash"></span>
</a>
</td>
</tr>
{foreachelse}
<tr>
<td colspan="4">{t}Prozatím nemáte žádné seznamy.{/t}</td>
</tr>
{/foreach}
</tbody>
</table>
{$url = path('kupshop_shoppinglist_shoppinglist_createshoppinglist')}
<button type="button" class="btn btn-primary" data-focus-opener="shopping-list">{t}Vytvořit nový seznam{/t}</button>
{include "focus/focus.add-shopping-list.tpl" class="shopping-list" onlyCreate=true}
{/block}
{block "js-entry" append}
{encore_entry_script_tags entry='shopping_list'}
{/block}

View File

@@ -0,0 +1,128 @@
{$body.products = $body.products->getProducts()}
<div class="shopping-list-links">
{* <a href="" data-wpj-focus="{path('kupshop_content_focus_focuslazy', ['template' => 'product-delivery', 'productId' => $product.id, 'variationId' => $product.variationId])}">{t}Kdy zboží obdržím?{/t}</a>*}
<a data-wpj-focus="{path('kupshop_content_focus_focuslazy', ['template' => 'shopping-list-share', 'listId'=>$body.listId, 'listHash'=>$body.hash, 'listName' => $view->getTitle()])}" href="">
<span class="fc icons_share"></span> {t}Sdílet seznam{/t}</a>
{if $view->isUserOwner($body.listId)}
{if $view->getLabel() != 'favorites'}
<a data-wpj-focus="{path("kupshop_content_focus_focuslazy", ['template' => 'shopping-list-rename','listId' => $body.listId, 'listName' => $view->getTitle()])}" href=""
data-wpj-focus-remove-on-close="1">
<span class="fc icons_file-txt"></span> {t}Přejmenovat seznam{/t}</a>
<a href="{path('kupshop_shoppinglist_shoppinglist_remove', ['id'=>$body.listId])}"
data-info-message="{t}Opravdu chcete smazat seznam{/t}"
data-list-delete="{$view->getTitle()|default:"{t}bez názvu{/t}"}">
<span class="fc icons_trash"></span> {t}Smazat seznam{/t}</a>
{/if}
{/if}
<span>{$body.products|count} {t plural="položky" plural5="položek" count=$body.products|count}položka{/t}</span>
</div>
<form action="{path('kupshop_shoppinglist_shoppinglist_addshoppinglisttocart')}" method="post">
<input type="hidden" value="{$body.listId}" name="listId">
<div class="shopping-list-products">
{foreach $body.products as $product}
<div class="product">
<div class="image">
<a href="{url s=product IDproduct=$product.id TITLE=$product.title}"
title="{t}Zobrazit zboží{/t}">
{$photo_type = 'product_catalog'}
{if $cfg.Photo.types['product_cart']}
{$photo_type = 'product_cart'}
{/if}
{$photo_dimensions = $cfg.Photo.types[$photo_type].size}
<img src="{get_photo photo=$product.image size=$photo_type}" alt="{$product.title}" class="img-responsive"
width="{$photo_dimensions[0]}" height="{$photo_dimensions[1]}">
</a>
</div>
<div class="title">
<a href="{url s=product IDproduct=$product.id TITLE=$product.title}"
title="{t}Zobrazit produkt{/t}">{$product.title}</a>
{if $product.variationTitle}
<span class="variation">{$product.variationTitle}</span>
{/if}
</div>
<div class="availability">
{block "availability"}
{if $product.inStore > 0}
{$inStore = $product.inStore|round:$product.unit.pieces_precision}
{if $product.in_store_show_max && $inStore > $product.in_store_show_max}
{$inStore = $product.in_store_show_max|round:$product.unit.pieces_precision}
{/if}
{if $dbcfg.inStore_max_visible and $inStore > $dbcfg.inStore_max_visible}
{$inStore = "> $inStore"}
{/if}
<span class="delivery delivery-0">{t}skladem{/t} {$inStore}
{$product.unit.short_name|default:"{t}ks{/t}"}</span>
{else}
<span class="delivery delivery-{$product.deliveryTime}">{$product.deliveryTimeText}</span>
{/if}
{/block}
</div>
<div class="in-cart">
{$product.in_cart|round:$product.unit.pieces_precision} {$product.unit.short_name|default:"{t}ks{/t}"} {t}v košíku{/t}
</div>
<div class="price">
{$product.productPrice|format_price}
</div>
<div class="pieces" data-buy_count="wrapper">
{$step = 1}
{ifmodule PRODUCTS__STEP}
{if $item && $item.product}
{$data = $item.product->getData()}
{$step = $data.step|default:$step}
{/if}
{/ifmodule}
<input type="number" name="products[{$product.id_slp}]" value="{$product.pieces|round:$product.unit.pieces_precision}"
class="form-control" min="{$step}" step="{$step}" data-buy_count="input">
<div class="buy_count">
<button type="button" class="fc plus plus_unit icons_caret_up" title="{t mnozstvi=$step}Přidat {mnozstvi} ks{/t}"
data-cart="plus"></button>
<button type="button" class="fc minus minus_unit icons_caret_down"
title="{t mnozstvi=$step}Odebrat {mnozstvi} ks{/t}"
data-cart="minus"></button>
</div>
</div>
<div class="btns">
{$addToCart = ""}
{if $product.has_variations && !$product.variationId}
{* nejde vlozit do kosiku - neni vybrana varianta *}
{$addToCart = "disabled"}
{ifmodule COMPONENTS}
{* modal pro vyber varianty a pridani do kosiku *}
{$addToCart = "data-wpj-modal=/_modal/submit-block-modal?id_product=`$product.id`"}
{/ifmodule}
{/if}
<button type="submit" name="addProduct" class="btn-reset" title="{t}Vložit do košíku{/t}"
value="{$product.id_slp}" {$addToCart}>
<span class="fc icons_cart" {if $addToCart}style="color: #fc3884;"
data-tooltip="{t}Vyberte variantu{/t}" title="{t}Vyberte variantu{/t}"{/if}></span>
</button>
{if $view->isUserOwner($body.listId)}
<a href="{path('kupshop_shoppinglist_shoppinglist_removeproduct', ['id'=>$product.id_slp])}"
title="{t}Odstranit z nákupního seznamu{/t}">
<span class="fc icons_trash"></span>
</a>
{/if}
</div>
</div>
{foreachelse}
<div class="alert alert-info">{t}Prozatím nemáte v seznamu žádné produkty.{/t}</div>
{/foreach}
</div>
{if $body.products|count}
<div class="shopping-list-buttons">
{if $view->isUserOwner($body.listId)}
<button type="submit" name="updateCount" class="btn btn-primary" value="1">{t}Uložit počty ks v seznamu{/t}</button>
{/if}
{ifmodule COMPONENTS}
{* Prozatím skrýt tlačítko Vložit vše do košíku *}
{elsemodule}
<button type="submit" name="addAllProducts" class="btn btn-ctr" value="1">{t}Vložit vše do košíku{/t}</button>
{/ifmodule}
</div>
{/if}
</form>

View File

@@ -0,0 +1,13 @@
{extends "account/account-wrapper.tpl"}
{block "title"}
<h1>{$view->getTitle()}</h1>
{/block}
{block "account-content"}
{include 'shoppingList/shopping-list-items.tpl'}
{/block}
{block "js-entry" append}
{encore_entry_script_tags entry='shopping_list'}
{/block}

View File

@@ -0,0 +1,9 @@
{extends "index.tpl"}
{block "content"}
{include 'shoppingList/shopping-list-items.tpl'}
{/block}
{block "js-entry" append}
{encore_entry_script_tags entry='shopping_list'}
{/block}

View File

@@ -0,0 +1,196 @@
<?php
namespace KupShop\ShoppingListBundle\Resources\upgrade;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\ShoppingListBundle\Util\ShoppingListUtil;
use Query\Operator;
class ShoppingListUpgrade extends \UpgradeNew
{
public function check_ProductsShoppingListTable()
{
return $this->checkTableExists('shopping_list');
}
/** Create 'shopping_list' table */
public function upgrade_ProductsShoppingListTable()
{
sqlQuery('create table shopping_list
(
id int auto_increment,
id_user int(11) unsigned default 0 not null,
name varchar(255) null,
constraint shopping_list_pk
primary key (id)
);
create unique index shopping_list_id_uindex
on shopping_list (id);
');
$this->upgradeOK();
}
public function check_ProductsShoppingListProductsTable()
{
return $this->checkTableExists('shopping_list_products');
}
/** Create 'shopping_list' table */
public function upgrade_ProductsShoppingListProductsTable()
{
sqlQuery('create table shopping_list_products
(
id int auto_increment null,
id_shopping_list int null,
id_product int not null,
id_variation int default null null,
pieces decimal(15, 4) default 0.0000 null,
date_created datetime default current_timestamp null,
constraint products_shoppig_list_products_pk
primary key (id)
);');
sqlQuery('create unique index shopping_list_uniq_prod on shopping_list_products (id_shopping_list, id_product);');
sqlQuery('create unique index shopping_list_uniq on shopping_list_products (id_shopping_list, id_product, id_variation);');
sqlQuery('create unique index shopping_list_products_id_uindex on shopping_list_products (id);');
sqlQuery('alter table shopping_list_products
add constraint shopping_list_products_shopping_list_id_fk
foreign key (id_shopping_list) references shopping_list (id) on update cascade on delete cascade;');
sqlQuery('alter table shopping_list_products
add constraint shopping_list_products_products_id_fk
foreign key (id_product) references products (id) ON UPDATE CASCADE ON DELETE CASCADE;');
sqlQuery('alter table shopping_list_products
add constraint shopping_list_products_products_variations_id_fk
foreign key (id_variation) references products_variations (id) ON UPDATE CASCADE ON DELETE CASCADE;');
$this->upgradeOK();
}
public function check_shoppingListProductsCascade()
{
return $this->checkConstraintRule('shopping_list_products', 'shopping_list_products_products_variations_id_fk', 'CASCADE');
}
/** change shopping_list_products_products_id_fk constraint to CASCADE */
public function upgrade_shoppingListProductsCascade()
{
sqlQuery('ALTER TABLE shopping_list_products DROP CONSTRAINT shopping_list_products_products_id_fk');
sqlQuery('ALTER TABLE shopping_list_products DROP CONSTRAINT shopping_list_products_products_variations_id_fk');
sqlQuery('ALTER TABLE shopping_list_products
ADD CONSTRAINT shopping_list_products_products_id_fk
FOREIGN KEY (id_product) REFERENCES products (id)
ON UPDATE CASCADE ON DELETE CASCADE');
sqlQuery('ALTER TABLE shopping_list_products
ADD CONSTRAINT shopping_list_products_products_variations_id_fk
FOREIGN KEY (id_variation) REFERENCES products_variations (id)
ON UPDATE CASCADE ON DELETE CASCADE');
$this->upgradeOK();
}
public function check_ColumnDate()
{
return $this->checkColumnExists('shopping_list', 'date');
}
/** Add index on Orders.status */
public function upgrade_ColumnDate()
{
sqlQuery('alter table shopping_list add date date null;');
$this->upgradeOK();
}
public function check_ColumnNote()
{
return $this->checkColumnExists('shopping_list', 'note');
}
/** Add index on Orders.status */
public function upgrade_ColumnNote()
{
sqlQuery('alter table shopping_list add note text null;');
$this->upgradeOK();
}
public function check_ShoppingListHashColumn()
{
return $this->checkColumnExists('shopping_list', 'hash');
}
/** Migrate shopping list hashes into database */
public function upgrade_ShoppingListHashColumn()
{
sqlQuery('ALTER TABLE shopping_list ADD COLUMN hash VARCHAR(60)');
$this->upgradeOK();
}
public function check_ShoppingListDropIndexUniqProd()
{
return !$this->checkIndexNameExists('shopping_list_products', 'shopping_list_uniq_prod');
}
/** Drop unique index id_shopping_list id_product */
public function upgrade_ShoppingListDropIndexUniqProd()
{
sqlQuery('DROP INDEX shopping_list_uniq_prod ON shopping_list_products');
$this->upgradeOK();
}
public function check_ShoppingListLabelColumn()
{
return $this->checkColumnExists('shopping_list', 'label');
}
/** Add label column to shopping_list table */
public function upgrade_ShoppingListLabelColumn()
{
sqlQuery('ALTER TABLE shopping_list ADD COLUMN label VARCHAR(50) DEFAULT NULL');
$this->upgradeOK();
}
public function check_FavoritesShoppingList()
{
return findModule(\Modules::COMPONENTS) && !$this->checkTableExists('products_favorites');
}
/** Migrate products favorites to shopping lists */
public function upgrade_FavoritesShoppingList()
{
$shoppingListUtil = ServiceContainer::getService(ShoppingListUtil::class);
$list_name = translate('title', 'category')['favorites'] ?? 'Favorites';
$users = sqlQueryBuilder()->select('DISTINCT id_user')->from('products_favorites');
foreach ($users->execute() as $user) {
$id_user = $user['id_user'];
$id_list = sqlQueryBuilder()->select('id')->from('shopping_list')
->andWhere(Operator::equals(['id_user' => $id_user, 'label' => 'favorites']))
->execute()->fetchOne();
if (!$id_list) {
$created = date('Y-m-d');
sqlQueryBuilder()->insert('shopping_list')
->directValues([
'id_user' => $id_user,
'date' => $created,
'name' => $list_name,
'label' => 'favorites',
])->execute();
$id_list = intval(sqlInsertId());
$shoppingListUtil->updateHash($id_list, $created);
}
sqlQuery("INSERT IGNORE INTO shopping_list_products (id_shopping_list, id_product, pieces)
SELECT {$id_list}, id_product, 1
FROM products_favorites
WHERE id_user = '{$id_user}'");
sqlQuery("DELETE FROM products_favorites WHERE id_user = '{$id_user}'");
}
// delete products_favorites
$this->upgradeOK();
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace KupShop\ShoppingListBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ShoppingListBundle extends Bundle
{
}

View File

@@ -0,0 +1,395 @@
<?php
declare(strict_types=1);
namespace KupShop\ShoppingListBundle\Util;
use KupShop\CatalogBundle\ProductList\ProductCollection;
use KupShop\GraphQLBundle\EventListener\JsShopRefreshListener;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\OrderingBundle\Cart;
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
use KupShop\ShoppingListBundle\Email\ShareShoppingListEmail;
use KupShop\ShoppingListBundle\Entity\ShoppingListEntity;
use Query\Operator;
use Query\QueryBuilder;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Contracts\Service\Attribute\Required;
class ShoppingListUtil
{
#[Required]
public ShoppingListEntity $shoppingListEntity;
#[Required]
public PurchaseUtil $purchaseUtil;
#[Required]
public Cart $cart;
#[Required]
public UserContext $userContext;
#[Required]
public SessionInterface $session;
#[Required]
public ShareShoppingListEmail $shareShoppingListEmail;
public function getShoppingList(?int $id): ShoppingListEntity
{
$sl = $this->shoppingListEntity;
$list = sqlQueryBuilder()->select('*')
->from('shopping_list')
->where(
Operator::equals(
['id' => $id]
)
)
->execute()->fetch();
if (!$list) {
return $sl;
}
$sl->setId($list['id']);
$sl->setIdUser($list['id_user']);
$sl->setName($list['name']);
$sl->setNote($list['note']);
$sl->setDate($list['date']);
$sl->setHash($list['hash']);
$sl->setLabel($list['label']);
return $sl;
}
public function processShoppingListItems($id_shopping_list, $items, $duplicate = false)
{
if (is_iterable($items)) {
foreach ($items as $id => $item) {
if ($id == 0) {
continue;
}
if ($id > 0 && isset($item['delete'])) {
$this->removeProductFromList($item['id']);
} elseif ($id > 0 && !$duplicate) {
sqlQueryBuilder()->update('shopping_list_products')
->set('pieces', $item['pieces'])
->where(Operator::equals(['id' => $item['id']]))->execute();
} elseif (!empty($item['id_product'])) {
$this->addProductToShoppingList(
(int) $id_shopping_list,
(int) $item['id_product'],
(int) $item['id_variation'] ?: null,
floatval($item['pieces']));
}
}
}
}
public function renameShoppingList($id_shopping_list, $new_name): void
{
sqlQueryBuilder()->update('shopping_list')->directValues(['name' => $new_name])->andWhere(Operator::equals(['id' => $id_shopping_list]))->execute();
}
public function validHash(int $slId, string $hash): bool
{
return (bool) sqlQueryBuilder()->select('count(id)')
->from('shopping_list')
->where(Operator::equals([
'id' => $slId,
'hash' => $hash,
]))
->execute()->fetchOne();
}
public function createShoppingList(string $name, ?string $label = null): int
{
$created = date('Y-m-d');
sqlQueryBuilder()->insert('shopping_list')
->directValues([
'id_user' => $this->userContext->getActiveId(),
'date' => $created,
'name' => $name,
'label' => $label,
])->execute();
$insertedId = intval(sqlInsertId());
$this->updateHash($insertedId, $created);
return $insertedId;
}
public function updateHash(int $id, string $created): void
{
sqlQueryBuilder()
->update('shopping_list')->set('hash', ':hash')
->setParameter('hash', $this->createHash($id, $created))
->where(\Query\Operator::equals(['id' => $id]))
->execute();
}
public function createHash(int $id, string $created)
{
return urlencode(
sha1(
getShopUniqueName()
.join('', [
$id,
$created,
])
)
);
}
public function fetchItems(int $idSL, $fromAdmin = false): \ProductList
{
$productList = new \ProductList(findModule(\Modules::PRODUCTS_VARIATIONS));
if (!$fromAdmin) {
$productList->applyDefaultFilterParams();
}
$productList->andSpec(function (QueryBuilder $qb) use ($idSL) {
$qb->join('p', 'shopping_list_products', 'slp', 'slp.id_product = p.id')
->andWhere('id_shopping_list=:id_shopping_list')
->addParameters(['id_shopping_list' => $idSL]);
if (findModule(\Modules::PRODUCTS_VARIATIONS)) {
$qb->andWhere('slp.id_variation IS NULL OR slp.id_variation = pv.id');
$qb->addSelect('slp.id_variation');
$qb->addSelect(\Query\Product::getInStoreField(false, $qb).' AS in_store_product');
$qb->groupBy('p.id, slp.id_variation');
}
$qb->addSelect('slp.id as id_slp, slp.pieces as slp_pieces');
if ($in_store_show_max = findModule(\Modules::PRODUCTS, \Modules::SUB_SHOW_MAX)) {
$qb->addSelect("COALESCE(p.in_store_show_max, {$in_store_show_max}) AS in_store_show_max");
}
});
$productList->addResultModifiers(function (ProductCollection $products, $dataAll) use ($fromAdmin) {
if (!$fromAdmin && ($products->count() > 0)) {
$this->countItemsInCart($products);
}
foreach ($dataAll as $key => $data) {
$products[$key]['id_slp'] = $data['id_slp'];
$products[$key]['pieces'] = $data['slp_pieces'];
if (isset($data['in_store_show_max'])) {
$products[$key]['in_store_show_max'] = $data['in_store_show_max'];
}
if (($data['has_variations'] ?? false) && empty($data['id_variation'])) {
// kdyz produkt ma varianty, ale v nakupnim seznamu je bez vybrane varianty:
// inStore a deliveryTimeText jsou od varianty (matched_id_variation), protoze productList ma variationsAsResult=true,
// potrebujeme upravit inStore a deliveryTimeText, aby byly od produktu
$products[$key]['inStore'] = $data['in_store_product'];
unset($products[$key]['in_store_suppliers']);
$products[$key]->prepareDeliveryText();
}
}
});
$cfg = Config::get();
$mainSize = 'product_cart';
if (empty($cfg['Photo']['types']['product_cart'])) {
$mainSize = 'product_catalog';
}
$productList->fetchImages($mainSize, fallbackToProductPhoto: true);
return $productList;
}
private function countItemsInCart(&$products)
{
$products->forAll(function ($key, &$product) {
$product['in_cart'] = 0;
return true;
});
if (is_null($this->cart->only_virtual_products)) {
// not yet initialized, but purchaseState could be set somehow...
$this->cart->invalidatePurchaseState();
}
foreach ($this->purchaseUtil->getPurchaseState()->getProducts() as $cartItems) {
$key = $cartItems->getIdProduct();
if (isset($products[$key])) {
$products[$key]['in_cart'] += $cartItems->getPieces();
}
if ($idVariation = $cartItems->getIdVariation()) {
$key .= '/'.$idVariation;
if (isset($products[$key])) {
$products[$key]['in_cart'] += $cartItems->getPieces();
}
}
}
}
public function removeProductFromList(int $id)
{
return sqlQueryBuilder()->delete('shopping_list_products')
->where(Operator::equals(['id' => $id]))
->execute();
}
public function getProductlistIdForProduct(int $idSlProduct)
{
return sqlQueryBuilder()->select('id_shopping_list')
->from('shopping_list_products')
->where(Operator::equals(['id' => $idSlProduct]))->execute()->fetchOne();
}
public function getShoppingListLabel(int $id)
{
return sqlQueryBuilder()->select('label')
->from('shopping_list')
->where(Operator::equals(['id' => $id]))->execute()->fetchOne();
}
public function deleteShoppingList(int $id)
{
return sqlQueryBuilder()->delete('shopping_list')
->where(Operator::equals(['id' => $id]))
->execute();
}
public function isUserOwner(int $idList): bool
{
if ($userId = $this->userContext->getActiveId()) {
if (sqlQueryBuilder()->select('count(id)')
->from('shopping_list')->where(
Operator::equals(['id_user' => $userId, 'id' => $idList])
)->execute()->fetchOne()) {
return true;
}
}
return false;
}
public function addProductToShoppingList(int $listId, int $productId, ?int $variationId = null, float $pieces = 1): bool
{
if ($existId = sqlQueryBuilder()->select('id')
->from('shopping_list_products')
->where(
Operator::equalsNullable(
['id_shopping_list' => $listId, 'id_product' => $productId, 'id_variation' => $variationId]
)
)
->execute()->fetchOne()) {
return (bool) sqlQueryBuilder()->update('shopping_list_products')
->set('pieces', 'pieces+'.$pieces)
->where(Operator::equals(['id' => $existId]))->execute();
}
return (bool) sqlQueryBuilder()->insert('shopping_list_products')
->directValues([
'id_shopping_list' => $listId,
'id_product' => $productId,
'id_variation' => $variationId,
'pieces' => $pieces,
]
)->execute();
}
public function addItemsToCart(array $products)
{
foreach ($products as $id => $pieces) {
$product = $this->getShoppingListItem(intval($id));
if ($product) {
$item = ['id_product' => $product['id_product'], 'id_variation' => $product['id_variation'], 'pieces' => $pieces];
$this->addItemToCart($item);
}
}
}
public function updateShoppingList(array $products)
{
foreach ($products as $id => $pieces) {
sqlQueryBuilder()->update('shopping_list_products')
->set('pieces', $pieces)
->where(Operator::equals(['id' => $id]))->execute();
}
}
private function getShoppingListItem(int $id)
{
return sqlQueryBuilder()->select('id_product, id_variation')
->from('shopping_list_products')
->where(Operator::equals(['id' => $id]))
->execute()->fetch();
}
public function addToShoppingList(int $listId, array $products): void
{
foreach ($products as $product) {
$this->addProductToShoppingList(
$listId,
intval($product['productId']),
!empty($product['variationId']) ? intval($product['variationId']) : null,
floatval($product['pieces'] ?? 1)
);
}
}
public function addItemsFromCart($idList): void
{
foreach ($this->purchaseUtil->getPurchaseState()->getProducts() as $product) {
if ($product->getIdProduct()) {
$this->addProductToShoppingList((int) $idList, $product->getIdProduct(), $product->getIdVariation(), (float) $product->getPieces());
}
}
}
public function getHash($id)
{
return urlencode(
sha1(
getShopUniqueName()
.join('', [
$id,
date('Y-m-d'),
])
)
);
}
public function addItemToCart($item): void
{
$id = $this->cart->addItem($item);
if ($id <= 0) {
addUserMessage(translate('error', 'order')['invalid_variation'], 'error');
return;
}
if (findModule(\Modules::JS_SHOP)) {
$this->session->set(JsShopRefreshListener::SESSION_NAME, true);
}
}
public function getFavoritesShoppingList(): ?array
{
$id_user = $this->userContext->getActiveId();
if (!$id_user) {
return null;
}
$list = sqlQueryBuilder()->select('*')->from('shopping_list')
->andWhere(Operator::equals(['id_user' => $id_user, 'label' => 'favorites']))
->execute()->fetchAssociative();
return $list ?: null;
}
public function createFavoritesShoppingList(): int
{
return $this->createShoppingList(translate('title', 'category')['favorites'] ?? 'Favorites', 'favorites');
}
public function shareShoppingList(int $listId, string $emailTo, string $from): void
{
$this->shareShoppingListEmail->setListId($listId);
$this->shareShoppingListEmail->setFrom($from);
$email = $this->shareShoppingListEmail->getEmail();
$email['to'] = $emailTo;
$this->shareShoppingListEmail->sendEmail($email);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace KupShop\ShoppingListBundle\View;
use KupShop\KupShopBundle\Views\View;
class ShoppingListCreateView extends View
{
protected $template = 'shoppingList/create-list.tpl';
public function getTitle()
{
return translate('title_create', 'shopping_list');
}
public function getBodyVariables()
{
$vars = parent::getBodyVariables();
return $vars;
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace KupShop\ShoppingListBundle\View;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Views\Traits\RequestTrait;
use KupShop\KupShopBundle\Views\View;
use KupShop\ShoppingListBundle\Entity\ShoppingListEntity;
use KupShop\ShoppingListBundle\Util\ShoppingListUtil;
class ShoppingListProductsView extends View
{
use RequestTrait;
protected string $smartyFallback = 'account';
protected string $entrypoint = 'account';
protected $idList;
protected $template = 'shoppingList/shopping-list-products.tpl';
private $user;
public function __construct(public readonly ShoppingListUtil $shoppingListUtil, public ShoppingListEntity $shoppingListEntity, public UserContext $userContext)
{
}
public function getTitle()
{
return $this->shoppingListEntity->getName() ?: translate('products_title', 'shopping_list');
}
public function getLabel()
{
return $this->shoppingListEntity->getLabel();
}
public function getNote()
{
return $this->shoppingListEntity->getNote() ?: null;
}
public function getBodyVariables()
{
$vars = parent::getBodyVariables();
$this->shoppingListEntity = $this->shoppingListUtil->getShoppingList($this->getIdList());
$vars['products'] = $this->shoppingListUtil->fetchItems($this->getIdList());
$vars['listId'] = $this->getIdList();
$vars['hash'] = $this->shoppingListEntity->getHash();
return $vars;
}
private function getUser()
{
if ($this->user) {
return $this->user;
}
return $this->user = $this->userContext->getActive();
}
public function getIdList()
{
return $this->idList;
}
public function setIdList($idList): void
{
$this->idList = $idList;
}
public function getBreadcrumbs()
{
return getReturnNavigation(-1,
'USER',
[['link' => path('kupshop_shoppinglist_shoppinglist_shoppinglist'), 'text' => translate('title', 'shopping_list')],
$this->getTitle(), ]);
}
public function isUserOwner()
{
return $this->shoppingListUtil->isUserOwner($this->getIdList());
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace KupShop\ShoppingListBundle\View;
use KupShop\KupShopBundle\Views\Traits\RequestTrait;
class ShoppingListSharedView extends ShoppingListProductsView
{
use RequestTrait;
protected $idList;
protected $template = 'shoppingList/shopping-list-shared-products.tpl';
}

View File

@@ -0,0 +1,59 @@
<?php
namespace KupShop\ShoppingListBundle\View;
use KupShop\KupShopBundle\Views\View;
class ShoppingListView extends View
{
protected string $smartyFallback = 'account';
protected string $entrypoint = 'account';
protected $template = 'shoppingList/list.tpl';
private $user;
private $checkUserLog;
public function getTitle()
{
return translate('title', 'shopping_list');
}
public function getBodyVariables()
{
$vars = parent::getBodyVariables();
$this->checkUserLog();
$this->getUser();
$vars['lists'] = $this->getUserShoppingLists();
return $vars;
}
private function getUserShoppingLists()
{
return $this->user->getShoppingLists();
}
protected function checkUserLog()
{
if ($this->checkUserLog) {
if (!$this->getUser()) {
redirection('LOGIN');
}
}
}
private function getUser()
{
if ($this->user) {
return $this->user;
}
return $this->user = \User::getCurrentUser();
}
public function getBreadcrumbs()
{
return getReturnNavigation(-1, 'USER', [$this->getTitle()]);
}
}