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,157 @@
<?php
declare(strict_types=1);
namespace KupShop\WarehouseBundle\Admin\Actions;
use KupShop\AdminBundle\Admin\Actions\AbstractAction;
use KupShop\AdminBundle\Admin\Actions\ActionResult;
use KupShop\AdminBundle\Admin\Actions\IAction;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\WarehouseBundle\Util\ExpiringProductUtil;
use Query\Operator;
class CreateExpiringProductWarehouse extends AbstractAction implements IAction
{
/**
* @required
*/
public ExpiringProductUtil $expiringProductDMTUtil;
public function getTypes(): array
{
return ['products'];
}
public function getName(): string
{
return 'Vytvořit zlevněný produkt z šarže / pozice';
}
public function execute(&$data, array $config, string $type): ActionResult
{
$idProduct = (int) $this->getId();
$idPosition = ($config['selectedPosition'] > 0) ? (int) $config['selectedPosition'] : null;
$idBatch = ($config['selectedBatch'] > 0) ? (int) $config['selectedBatch'] : null;
$discount = (float) $config['discount'];
$prependText = $config['prependText'];
try {
if ($this->expiringProductDMTUtil->productHasEnabledBatches($this->getId())) {
$newProductData = $this->expiringProductDMTUtil->createFromBatch($idProduct, $idBatch, $idPosition, $prependText);
} else {
$newProductData = $this->expiringProductDMTUtil->createFromPosition($idProduct, $idBatch, $idPosition, $prependText);
}
if ($discount) {
$this->expiringProductDMTUtil->addDiscountToProduct($newProductData['oldIdProduct'],
$newProductData['oldIdVariation'],
$newProductData['idProduct'],
$newProductData['idVariation'],
$discount);
}
$this->expiringProductDMTUtil->hideSoldExpiringProduct($newProductData['idProduct']);
$result = new ActionResult(true,
sprintf('Zlevněný produkt úspěšně vytvořen. Bylo přesunuto %d kusů', $newProductData['movedPieces'])
);
$result->setRedirect(['ID' => $newProductData['idProduct']]);
addActivityLog(ActivityLog::SEVERITY_SUCCESS,
ActivityLog::TYPE_CHANGE,
sprintf('Vytvořen zlevněný produkt s ID %d', $newProductData['idProduct']));
} catch (\Exception $e) {
$result = new ActionResult(false);
addActivityLog(ActivityLog::SEVERITY_ERROR,
ActivityLog::TYPE_CHANGE,
sprintf('Nepodařilo se vytvořit zlevněný produkt z produktu s ID %d', $idProduct));
getRaven()->captureException($e);
}
return $result;
}
public function getVars(): array
{
$positions = sqlFetchAll(sqlQueryBuilder()->select('wp.id_position, wpos.code')
->from('warehouse_products', 'wp')
->leftJoin('wp', 'warehouse_positions', 'wpos', 'wp.id_position = wpos.id')
->leftJoin('wpos', 'warehouse_locations', 'wl', 'wpos.id_location = wl.id')
->where(Operator::equals(['wp.id_product' => $this->getId()]))
->andWhere('wp.pieces > 0')
->groupBy('wp.id_position')
->orderBy("wl.code = 'BOX'")
->execute(),
'id_position');
$hasBatches = $this->expiringProductDMTUtil->productHasEnabledBatches($this->getId());
foreach ($positions as &$position) {
$qb = sqlQueryBuilder()->from('products', 'p')
->leftJoin('p', 'products_variations', 'pv', 'p.id = pv.id_product')
->leftJoin('p', 'warehouse_products', 'wp', 'p.id=wp.id_product AND pv.id <=> wp.id_variation')
->where(Operator::equals([
'p.id' => $this->getId(),
'wp.id_position' => $position['id_position'],
]))
->andWhere('wp.pieces > 0')
->andWhere('COALESCE(pv.in_store, p.in_store) > 0')
->groupBy('pv.id');
if ($hasBatches) {
$qb->leftJoin('wp', 'products_batches', 'pb', 'wp.id_product = pb.id_product AND wp.id_variation <=> pb.id_variation AND wp.id_product_batch = pb.id');
$qb->select('pb.id, pb.id id_batch,pb.date_expiry, pb.id_product,pb.id_variation,
CONCAT(COALESCE(pv.title, "Produkt"), ": ", pb.code, " (", DATE_FORMAT(pb.date_expiry, "%d.%m.%Y"), ")") title');
$qb->addGroupBy('pb.id');
} else {
$qb->select('pv.id id, pv.id id_variation, COALESCE(pv.title, "Produkt") title');
}
$qb->addSelect('COALESCE(pv.price, p.price) price, p.vat');
$position['variations'] = sqlFetchAll($qb->execute(), 'id');
$position['variations'] = array_map(function ($var) {
$var['vat'] = getVat($var['vat']);
return $var;
}, $position['variations']);
if ($hasBatches) {
foreach ($position['variations'] as &$batch) {
$batch['alreadyExists'] = $this->expiringProductDMTUtil->expiringProductWithDMTAlreadyExists($batch['id_product'],
$batch['id_variation'],
$batch['date_expiry']) ?: false;
}
}
}
$positions = array_filter($positions, fn ($val) => $val['variations']);
$allVariations = [];
foreach ($positions as $idPos => $pos) {
foreach ($pos['variations'] as $variation) {
if (empty($allVariations[$variation['id']])) {
$variation['availablePieces'] = $this->expiringProductDMTUtil->getAvailablePiecesToMoveWarehouse($this->getId(),
$variation['id_variation'],
$variation['id_batch'] ?? false);
if ($variation['availablePieces'] > 0) {
$allVariations[$variation['id']] = $variation;
}
}
}
foreach ($pos['variations'] as $idVar => $variation) {
$positions[$idPos]['variations'][$idVar]['availablePieces'] = $this->expiringProductDMTUtil->getAvailablePiecesToMoveWarehouse($this->getId(),
$variation['id_variation'],
$variation['id_batch'] ?? false,
$pos['id_position']);
}
}
$productExists = $this->expiringProductDMTUtil->expiringProductAlreadyExists($this->getId());
return ['result' => ['positions' => $positions, 'hasBatches' => $hasBatches, 'allVariations' => $allVariations],
'productExists' => $productExists];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace KupShop\WarehouseBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
class PosWarehouseSettingsTab extends WindowTab
{
protected $title = 'flapPosWarehouseSettings';
protected $template = 'window/posWarehouseSettingsTab.tpl';
public function handleUpdate()
{
if (empty(getVal('data')['data']['virtual_box'] ?? null)) {
$this->returnError('Není vybrán virtuální box');
}
}
public function getLabel()
{
return translate($this->title, 'posSettings');
}
public static function getTypes()
{
return [
'posSettings' => 1,
];
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace KupShop\WarehouseBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
use KupShop\AdminBundle\Util\Stats\StatsDataLoader;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Query\Operator;
class StatsWarehouseTab extends WindowTab
{
use \DatabaseCommunication;
protected $title = 'flapWarehouse';
protected $template = 'window/statsWarehouseTab.tpl';
private $from;
private $to;
public static function getTypes()
{
return [
'stats' => 95,
];
}
public function getVars($smarty_tpl_vars)
{
$vars = [];
$format = \Settings::getDateFormat();
$this->from = $vars['from'] = getVal('fromWarehouse', null, date($format, time() - (365 * 86400)));
$this->to = $vars['to'] = getVal('toWarehouse', null, date($format));
if (getVal('fromWarehouse')) {
$vars['userData'] = $this->handleGetAdminData();
$vars['packingData'] = $this->handleGetPackingData();
}
return $vars;
}
public function handleGetWarehouseGraph()
{
$this->from = getVal('fromWarehouse');
$this->to = getVal('toWarehouse');
ServiceContainer::getService(StatsDataLoader::class)
->setTypeGraph(getVal('typegraph'))
->setFrom($this->from)
->setTo($this->to)
->setInterval(getVal('intervalWarehouse'))
->setTabInstance($this)
->getGraph();
}
public function handleGetPackingGraph()
{
$this->from = getVal('fromWarehouse');
$this->to = getVal('toWarehouse');
ServiceContainer::getService(StatsDataLoader::class)
->setTypeGraph(getVal('typegraph'))
->setFrom($this->from)
->setTo($this->to)
->setInterval(getVal('intervalWarehouse'))
->setTabInstance($this)
->getGraph();
}
public function handleGetAdminData()
{
return sqlQueryBuilder()->select('login, SUM((1 * GREATEST(wl.score_multiplier, wl2.score_multiplier))) as score')
->from('warehouse_log', 'wlog')
->leftJoin('wlog', 'admins', 'ad', 'wlog.id_admin = ad.id')
->leftJoin('wlog', 'warehouse_positions', 'wpos', 'wpos.id = wlog.old_id_position')
->leftJoin('wpos', 'warehouse_locations', 'wl', 'wl.id = wpos.id_location')
->leftJoin('wlog', 'warehouse_positions', 'wpos2', 'wpos2.id = wlog.new_id_position')
->leftJoin('wpos2', 'warehouse_locations', 'wl2', 'wl2.id = wpos2.id_location')
->where('login IS NOT NULL')
->andWhere('wlog.update_datetime >= :time_from')
->andWhere('wlog.update_datetime <= :time_to')
->setParameter('time_from', $this->prepareDate($this->from).' 00:00:00')
->setParameter('time_to', $this->prepareDate($this->to).' 23:59:59')
->groupBy('id_admin')
->orderBy('login', 'ASC')
->execute()
->fetchAll();
}
public function handleGetPackingData()
{
return sqlQueryBuilder()->select('a.login', 'COUNT(*) as score')
->from('warehouse_orders', 'wo')
->join('wo', 'admins', 'a', 'wo.id_packaged_by = a.id')
->where(Operator::between('wo.date_close', new \Range(
$this->prepareDate($this->from).' 00:00:00',
$this->prepareDate($this->to).' 23:59:59'
)))
->groupBy('a.id')
->execute()->fetchAll();
}
public function getDBWarehouseData()
{
if (getVal('packing')) {
return $this->getDBWarehousePackingData();
}
$SQL = sqlQueryBuilder()
->select('login, SUM((1 * GREATEST(wl.score_multiplier, wl2.score_multiplier))) as score, (YEAR(wlog.update_datetime)- YEAR(:from)) as year')
->from('warehouse_log', 'wlog')
->leftJoin('wlog', 'admins', 'ad', 'wlog.id_admin = ad.id')
->leftJoin('wlog', 'warehouse_positions', 'wpos', 'wpos.id = wlog.old_id_position')
->leftJoin('wpos', 'warehouse_locations', 'wl', 'wl.id = wpos.id_location')
->leftJoin('wlog', 'warehouse_positions', 'wpos2', 'wpos2.id = wlog.new_id_position')
->leftJoin('wpos2', 'warehouse_locations', 'wl2', 'wl2.id = wpos2.id_location')
->where('login IS NOT NULL')
->andWhere('wlog.update_datetime >= :from')
->andWhere('wlog.update_datetime <= :to')
->setParameters([
'from' => $this->prepareDate($this->from).' 00:00:00',
'to' => $this->prepareDate($this->to).' 23:59:59',
]);
switch (getVal('intervalWarehouse')) {
case 'month':
$SQL->addSelect('MONTH(wlog.update_datetime) as month')
->addGroupBy('MONTH(wlog.update_datetime)')
->addOrderBy('month');
break;
case 'year':
break;
case 'day':
default:
$SQL->addSelect('DAY(wlog.update_datetime) as day, MONTH(wlog.update_datetime) as month')
->addGroupBy('MONTH(wlog.update_datetime), DAY(wlog.update_datetime)')
->addOrderBy('month,day');
break;
}
$SQL->addGroupBy('login');
$SQL = $SQL->execute();
return $this->formatDataByUser($SQL);
}
public function formatDataByUser($SQL)
{
$data = [];
$interval = getVal('intervalWarehouse');
if ($interval == 'year') {
foreach ($SQL as $row) {
$data[$row['login']][$row['year']] = $row['score'];
}
} elseif ($interval == 'month') {
foreach ($SQL as $row) {
$data[$row['login']][$row['year']][$row['month']] = $row['score'];
}
} elseif ($interval == 'day') {
foreach ($SQL as $row) {
$data[$row['login']][$row['year']][$row['month']][$row['day']] = $row['score'];
}
}
return $data;
}
public function getDaysData(&$data, $DBData, $year, $month, $day)
{
$i = 0;
foreach ($DBData as $key => $userData) {
$data[$i]['name'] = $key;
$data[$i++]['data'][] = (!empty($DBData[$key][$year][$month][$day])) ? round($DBData[$key][$year][$month][$day], 2) : 0;
}
}
public function getMonthsData(&$data, $DBData, $year, $month)
{
$i = 0;
foreach ($DBData as $key => $userData) {
$data[$i]['name'] = $key;
$data[$i++]['data'][] = (!empty($DBData[$key][$year][$month])) ? round($DBData[$key][$year][$month], 2) : 0;
}
}
public function getYearsData(&$data, $DBData, $year)
{
$i = 0;
foreach ($DBData as $key => $userData) {
$data[$i]['name'] = $key;
$data[$i++]['data'][] = (!empty($DBData[$key][$year])) ? round($DBData[$key][$year], 2) : 0;
}
}
protected function getDBWarehousePackingData()
{
$qb = sqlQueryBuilder()->select('a.login as login, COUNT(*) score, (YEAR(wo.date_close)- YEAR(:from)) as year, MONTH(wo.date_close) as month, DAY(wo.date_close) as day')
->from('warehouse_orders', 'wo')
->join('wo', 'admins', 'a', 'wo.id_packaged_by = a.id')
->where(Operator::between('wo.date_close', new \Range(
$this->prepareDate($this->from).' 00:00:00',
$this->prepareDate($this->to).' 23:59:59'
)))
->groupBy('a.id')
->addParameters([
'from' => $this->prepareDate($this->from).' 00:00:00',
'to' => $this->prepareDate($this->to).' 23:59:59',
]);
switch (getVal('intervalWarehouse')) {
case 'month':
$qb->addGroupBy('month')
->addOrderBy('month');
break;
case 'year':
$qb->addGroupBy('year')
->addOrderBy('year');
break;
case 'day':
default:
$qb->addGroupBy('month, day')
->addOrderBy('month, day');
break;
}
$qb->addGroupBy('a.id');
$SQL = $qb->execute();
return $this->formatDataByUser($SQL);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace KupShop\WarehouseBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
class StoresLocationsTab extends WindowTab
{
protected $title = 'flapStoresLocations';
protected $template = 'window/storesLocationsTab.tpl';
public static function getTypes()
{
return [
'stores' => 1,
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace KupShop\WarehouseBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\System\UrlFinder;
class WarehouseAdminsWindowTab extends WindowTab
{
protected $title = 'flapWarehouse';
protected $template = 'window/warehouseAdminsWindowTab.tpl';
public static function getTypes()
{
return [
'admins' => 1,
];
}
public function getVars($smarty_tpl_vars)
{
$urlFinder = ServiceContainer::getService(UrlFinder::class);
$data['settings'] = '@@KS@@'.json_encode([
'url' => $urlFinder->shopUrlAbsolute('/_warehouse/store-app/'),
]);
return $data;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace KupShop\WarehouseBundle\Admin\Tabs;
use KupShop\AdminBundle\Admin\WindowTab;
use Query\Operator;
class WarehouseOrdersWindowTab extends WindowTab
{
protected $title = 'flapWarehouse';
protected $template = 'window/warehouseOrdersWindowTab.tpl';
public function isVisible()
{
$base = parent::isVisible();
$notInAdd = $this->getAction() !== 'add';
return $base && $notInAdd;
}
public function getVars($smarty_tpl_vars)
{
$data = [];
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$warehouseOrder = sqlQueryBuilder()
->select('*')
->from('warehouse_orders')
->where(Operator::equals(['id_order' => $this->getID()]))
->execute()
->fetchAssociative();
$state = json_decode($warehouseOrder['state'] ?? '', true);
$orderItems = $smarty_tpl_vars['order']->value->fetchItems() ?? [];
foreach ($state['checkouts'] ?? [] as $checkout) {
if (!empty($orderItems[$checkout['id_item']]) && !empty($checkout['productBatch']['code'])) {
$orderItems[$checkout['id_item']]['batches'][] = ($checkout['productBatch'] ?? []) + ['pieces' => $checkout['pieces']];
}
}
$data['itemsWithBatches'] = $orderItems;
}
return $data;
}
public static function getTypes()
{
return [
'orders' => 1,
];
}
}

View File

@@ -0,0 +1,5 @@
<?php
$txt_str['StockInCheck'] = [
'flapStockInCheck' => 'Kontrola naskladňující faktury',
];

View File

@@ -0,0 +1,6 @@
<?php
$txt_str['WarehouseInventory'] = [
'search' => 'Vyhledávání',
'navigation' => 'Inventura',
];

View File

@@ -0,0 +1,15 @@
<?php
$txt_str['WarehouseLog'] = [
'toolbar_list' => 'Seznam pozic',
'toolbar_add' => 'Přidat pozici',
'search' => 'Vyhledávání',
'searchCode' => 'Podle pozice',
'navigation' => 'Pohyby ve skladu',
'onlyManuallyEdited' => 'Pouze ruční úpravy',
'onlyReplacements' => 'Pouze vzniklé z vratek',
'onlyStockIn' => 'Pouze vzniklé z naskladnění',
'onlyCreation' => 'Pouze vzniky (přišlo do skladu)',
'onlyDeletion' => 'Pouze zániky (odešlo ze skladu)',
'activityLogTag' => 'Fyzický sklad',
];

View File

@@ -0,0 +1,9 @@
<?php
$txt_str['locations'] = [
'toolbar_list' => 'Seznam lokací',
'toolbar_add' => 'Přidat lokaci',
'flapLocationsLabel' => 'Lokace',
'scoreMultiplier' => 'Koeficient skóre',
'navigation' => 'Lokace',
];

View File

@@ -0,0 +1,11 @@
<?php
$txt_str['positions'] = [
'toolbar_list' => 'Seznam pozic',
'toolbar_add' => 'Přidat pozici',
'flapLocationsLabel' => 'Pozice',
'search' => 'Vyhledávání',
'searchCode' => 'Podle názvu',
'editProducts' => 'Uložit',
'navigation' => 'Pozice',
];

View File

@@ -0,0 +1,9 @@
<?php
$txt_str['statsWarehouseTab'] = [
'flapWarehouse' => 'Shipped',
'from' => 'Od',
'to' => 'Do',
'show' => 'Zobrazit',
'score' => 'Nasbírané body',
];

View File

@@ -0,0 +1,8 @@
<?php
$txt_str['storesLocationsTab'] = [
'flapStoresLocations' => 'Lokace',
'warehouseLocationAssignment' => 'Přiřazení lokací skladu',
'locations' => 'Lokace',
'selectLocation' => 'Vybrat lokaci',
];

View File

@@ -0,0 +1,15 @@
<?php
$txt_str['warehouseAdminsWindowTab'] = [
'flapWarehouse' => 'Sklad',
'LOC_DEL' => 'Smazání pozice',
'LOCATIONS' => 'Skladové pozice',
'WAR_PROD_EDIT' => 'Editovat produkty na pozicích',
'WAR_PROD_DEL' => 'Mazat produkty na pozicích',
'WAR_ORDER_EDIT' => 'Povolit editaci objednávky po výstupní kontrole',
'LOC' => 'Sklad',
'WAR_INV' => 'Inventura',
'WAR_EAN' => 'Povolit ve čtečce přeskočit načtení EANu',
];

View File

@@ -0,0 +1,7 @@
<?php
$txt_str['warehouseOrdersWindowTab'] = [
'flapWarehouse' => 'Sklad',
'note_warehouse' => 'Poznámka do skladu',
];

View File

@@ -0,0 +1,5 @@
<?php
$txt_str['warehouseProducts'] = [
'date_stock_in' => 'Poslední naskladnění',
];

View File

@@ -0,0 +1,7 @@
<?php
$txt_str['warehouse_inspections'] = [
'duplicateProductEAN' => 'Duplicitní EANy produktů: [%s]',
'productsWithoutVariations' => 'Variantní produkty v polohovatelném skladu nemají přiřazené varianty (%s):',
'productWithoutVariation' => 'ID produktu: %s, kód produktu: %s, pozice: %s, kusů: %s, název: %s',
];

View File

@@ -0,0 +1,36 @@
<?php
class LocationsList extends \KupShop\AdminBundle\AdminList\BaseList
{
use AdminListSortable;
protected $pageDivide = 9999;
protected $template = 'listSortable.tpl';
protected $tableDef = [
'id' => 'id',
'fields' => [
'Pořadí' => ['field' => 'sort_index', 'render' => 'renderPosition', 'size' => '70px'],
'Lokace' => ['field' => 'code'],
'Počet pozic' => ['field' => 'positions'],
],
];
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('wl.id, wl.code', 'COUNT(wpos.id) as positions')
->from('warehouse_locations', 'wl')
->leftJoin('wl', 'warehouse_positions', 'wpos', 'wl.id = wpos.id_location')
->groupBy('wl.id');
return $qb;
}
public function handleDrag()
{
$this->saveList(getVal('moved_item'), 'warehouse_locations', ['sort_index', 'code']);
exit;
}
}

View File

@@ -0,0 +1,59 @@
<?php
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\AdminBundle\AdminList\FiltersStorage;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
class PositionsList extends BaseList
{
use FiltersStorage;
protected $template = 'list/positions.tpl';
protected $tableDef = [
'id' => 'id',
'fields' => [
'Pozice' => ['field' => 'code'],
'Položek' => ['field' => 'items'],
'Inventura' => ['field' => 'inventory_date', 'size' => 1, 'render' => 'renderDate'],
'Tisk' => ['field' => 'id', 'render' => 'renderCheckbox'],
],
];
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('id, code, (SELECT COUNT(*) FROM warehouse_products WHERE id_position=wpos.id) items, wpos.inventory_date')
->from('warehouse_positions', 'wpos')
->groupBy('wpos.id');
if (getVal('search')) {
$qb->andWhere(\Query\Operator::like(['wpos.code' => '%'.getVal('search').'%']));
}
if (getVal('id_product')) {
$qb->leftJoin('wpos', 'warehouse_products', 'wp', 'wp.id_position = wpos.id')
->andWhere(\Query\Operator::equalsNullable([
'wp.id_product' => getVal('id_product'),
'wp.id_variation' => getVal('id_variation'),
]));
}
if (getVal('location')) {
$qb->andWhere(\Query\Operator::equals(['wpos.id_location' => getVal('location')]));
}
return $qb;
}
public function renderCheckbox($values, $column)
{
$value = $this->getListRowValue($values, $column['field']);
return HTML::create('input')
->attr('type', 'checkbox')
->attr('name', 'positions[]')
->attr('value', $value)
->attr('checked', 1);
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace KupShop\WarehouseBundle\Admin\lists;
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\AdminBundle\AdminList\FiltersStorage;
use KupShop\AdminBundle\Query\StockIn;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\WarehouseBundle\Util\InventoryWorker;
use Query\Operator;
use Query\QueryBuilder;
class WarehouseInventoryList extends BaseList
{
use FiltersStorage;
protected $tableDef = [
'id' => 'id',
'fields' => [
'Produkt' => ['field' => 'p_title', 'size' => 2, 'type' => 'product', 'type_id' => 'id_product'],
'Varianta' => ['field' => 'pv_title'],
'EAN' => ['field' => 'ean'],
'Počet kusů' => ['field' => 'inv_pieces'],
'Prodejní cena' => ['field' => 'price', 'render' => 'renderPricePreferred', 'class' => 'right'],
'Uživatel' => ['field' => 'login'],
'DPH' => ['field' => 'vat_value', 'spec' => 'v.vat as vat_value', 'visible' => 'N'],
],
];
protected $orderParam = [
'sort' => 'Počet kusů',
'direction' => 'DESC',
];
public function customizeTableDef($tableDef)
{
$tableDef = parent::customizeTableDef($tableDef);
$tableDef['fields']['Kód'] = ['field' => 'code', 'visible' => 'N', 'spec' => function (QueryBuilder $qb) {
$codeField = 'p.code';
if (findModule(\Modules::PRODUCTS_VARIATIONS, \Modules::SUB_CODE)) {
$codeField = 'COALESCE(pv.code, p.code) as code';
}
$qb->addSelect($codeField);
}];
$tableDef['fields']['Nákupní cena bez dph'] = ['field' => 'price_buy', 'render' => 'renderPrice', 'class' => 'right', 'visible' => 'N', 'spec' => function (QueryBuilder $qb) {
$filterDateTo = getVal('dateTo');
$date = null;
if ($filterDateTo) {
if ($tmpDate = \DateTime::createFromFormat('d.m.Y H:i:s', $filterDateTo)) {
$date = $tmpDate;
}
}
$qb->addSelect('SUM((wl.pieces*COALESCE(r.average_price, COALESCE(pv.price_buy, p.price_buy)))) as price_buy')
->leftJoinSubQuery('pv', StockIn::getAveragePriceSubQuery($date), 'r', 'r.id_product = p.id AND ((pv.id IS NULL AND r.id_variation IS NULL) OR pv.id = r.id_variation)');
}];
return $tableDef;
}
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('wl.*, p.title as p_title,
pv.title as pv_title, COALESCE(pv.ean, p.ean) as ean, a.id, GROUP_CONCAT(DISTINCT a.login) as login, p.vat,
SUM(wl.pieces) as inv_pieces, FLOOR(COALESCE(pv.price, p.price) * SUM(wl.pieces)) as price')
->from('warehouse_log', 'wl')
->leftJoin('wl', 'products', 'p', 'p.id=wl.id_product')
->leftJoin('wl', 'products_variations', 'pv', 'pv.id=wl.id_variation')
->join('p', 'vats', 'v', 'v.id = p.vat')
->leftJoin('wl', 'admins', 'a', 'wl.id_admin=a.id')
->addGroupBy('p.id, pv.id')
->andHaving('inv_pieces != 0');
$inventoryWorker = ServiceContainer::getService(InventoryWorker::class);
$inventoryPositionID = $inventoryWorker->getInventoryPosition();
$qb->andWhere(Operator::equals(['old_id_position' => $inventoryPositionID]));
if (getVal('id_product')) {
$qb->andWhere(\Query\Operator::equalsNullable(['wl.id_product' => getVal('id_product'), 'wl.id_variation' => getVal('id_variation') ?: null]));
}
if ($dateFrom = getVal('dateFrom')) {
$qb->andWhere('wl.update_datetime>=:dateFromParam')
->setParameter('dateFromParam', "{$this->prepareDateTime($dateFrom)}");
}
if ($dateTo = getVal('dateTo')) {
$qb->andWhere('wl.update_datetime<=:dateToParam')
->setParameter('dateToParam', "{$this->prepareDateTime($dateTo)}");
}
return $qb;
}
}
return WarehouseInventoryList::class;

View File

@@ -0,0 +1,114 @@
<?php
namespace KupShop\WarehouseBundle\Admin\lists;
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\AdminBundle\AdminList\FiltersStorage;
use Query\Operator;
class WarehouseLogList extends BaseList
{
use FiltersStorage;
protected $tableName = 'warehouse_log';
protected ?string $tableAlias = 'wl';
protected bool $useLazyNumberOfPages = true;
protected $tableDef = [
'id' => 'wl.id',
'fields' => [
'Produkt' => ['field' => 'p_title', 'size' => 2, 'type' => 'product', 'type_id' => 'id_product'],
'Varianta' => ['field' => 'pv_title'],
'Datum' => ['field' => 'update_datetime', 'renderer' => 'renderDateTime'],
'EAN' => ['field' => 'ean'],
'Počet kusů' => ['field' => 'pieces'],
'Z' => ['field' => 'old_code', 'type' => 'positions', 'type_id' => 'old_position'],
'Do' => ['field' => 'new_code', 'type' => 'positions', 'type_id' => 'new_position'],
'Objednávka' => ['field' => 'order_no', 'type' => 'orders', 'type_id' => 'id_order'],
'Uživatel' => ['field' => 'login'],
],
];
protected $orderParam = [
'sort' => 'Datum',
'direction' => 'DESC',
];
public function customizeTableDef($tableDef)
{
if (findModule(\Modules::STORES)) {
$tableDef['fields']['Převodka'] = ['field' => 'wl.id_transfer', 'type' => 'storesTransfers', 'type_id' => 'id_transfer'];
}
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$tableDef['fields']['Šarže'] = ['field' => 'batch_code', 'type' => 'productsBatches', 'type_id' => 'id_product_batch'];
}
return $tableDef;
}
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('wl.*, p.title as p_title,
pv.title as pv_title, COALESCE(pv.ean, p.ean) as ean, COALESCE(pv.code, p.code) as code,
wpos_old.code as old_code, wpos_old.id as old_position, wpos_new.code as new_code, wpos_new.id as new_position,
a.id, a.login, o.order_no')
->from('warehouse_log', 'wl')
->leftJoin('wl', 'warehouse_positions', 'wpos_new', 'wpos_new.id=wl.new_id_position')
->leftJoin('wl', 'warehouse_positions', 'wpos_old', 'wpos_old.id=wl.old_id_position')
->leftJoin('wl', 'products', 'p', 'p.id=wl.id_product')
->leftJoin('wl', 'products_variations', 'pv', 'pv.id=wl.id_variation')
->leftJoin('wl', 'admins', 'a', 'wl.id_admin=a.id')
->leftJoin('wl', 'orders', 'o', 'wl.id_order=o.id');
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$qb->addSelect('wl.id_product_batch, pb.code as batch_code')
->leftJoin('wl', 'products_batches', 'pb', 'pb.id=wl.id_product_batch');
}
if ($id_location = getVal('location')) {
$qb->andWhere(Operator::equals(['wpos_new.id_location' => $id_location, 'wpos_old.id_location' => $id_location], 'OR'));
}
if ($search = trim(getVal('search', default: ''))) {
$qb->andWhere(Operator::like(['wpos_new.code' => '%'.$search.'%', 'wpos_old.code' => '%'.$search.'%'], 'OR'));
}
if (getVal('id_product')) {
$qb->andWhere(\Query\Operator::equalsNullable(['wl.id_product' => getVal('id_product'), 'wl.id_variation' => getVal('id_variation') ?: null]));
}
if (getVal('onlyManuallyEdited')) {
$qb->andWhere(Operator::like(['wl.note' => '%handle_added%']));
}
if (getVal('onlyReplacements')) {
$qb->andWhere(Operator::equalsNullable(['wpos_old.code' => null, 'wpos_new.code' => 'VRATKY']));
}
if (getVal('onlyStockIn')) {
$qb->andWhere(Operator::equalsNullable(['wpos_old.code' => null, 'wpos_new.code' => 'PRIJEM']));
}
if (getVal('onlyCreation')) {
$qb->andWhere(Operator::equalsNullable(['wpos_old.code' => null]));
}
if (getVal('onlyDeletion')) {
$qb->andWhere(Operator::equalsNullable(['wpos_new.code' => null]));
}
if ($order = getVal('order')) {
$qb->andWhere(Operator::like(['o.order_no' => "%{$order}%"]));
}
if ($dateFrom = getVal('dateFrom')) {
$qb->andWhere('wl.update_datetime>=:dateFromParam')
->setParameter('dateFromParam', "{$this->prepareDateTime($dateFrom)}");
}
if ($dateTo = getVal('dateTo')) {
$qb->andWhere('wl.update_datetime<=:dateToParam')
->setParameter('dateToParam', "{$this->prepareDateTime($dateTo)}");
}
return $qb;
}
}
return WarehouseLogList::class;

View File

@@ -0,0 +1,121 @@
<?php
namespace KupShop\WarehouseBundle\Admin\lists;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
use KupShop\WarehouseBundle\Util\StoreItemWorker;
use KupShop\WarehouseBundle\WarehouseBundle;
use Query\Operator;
class WarehouseMissingProductsList extends \KupShop\AdminBundle\AdminList\BaseList
{
protected $template = 'list/missing_products.tpl';
protected $tableDef = [
'id' => 'id',
'fields' => [
'Produkt' => ['field' => 'p_title', 'size' => 2, 'type' => 'product', 'type_id' => 'id_product'],
'Varianta' => ['field' => 'pv_title', 'size' => 1],
'EAN' => ['field' => 'ean'],
'Kód' => ['field' => 'code'],
'Pozice' => ['field' => 'positions', 'render' => 'renderPosition', 'size' => 2],
'Webový sklad ks' => ['field' => 'product_in_store', 'size' => 0.5],
'Fyzický sklad ks' => ['field' => 'warehouse_in_store', 'size' => 0.5],
'Kusů v obj.' => ['field' => 'orders_in_store', 'size' => 0.5],
],
];
public function getSQLOrdering(&$var, &$orderParam)
{
parent::getSQLOrdering($var, $orderParam);
if (($var['orderField'] ?? '') == 'positions') {
$var['orderField'] = '';
}
}
public function get_vars()
{
$vars = parent::get_vars();
$vars['showNotInStore'] = getVal('showNotInStore');
return $vars;
}
public function getQuery()
{
$worker = ServiceContainer::getService(StoreItemWorker::class);
$qb = $worker->getMissingProductsQuery(getVal('showNotInStore'))
->select('p.id as id_product, pv.id as id_variation, p.title as p_title, pv.title as pv_title, COALESCE(pv.ean, p.ean) as ean, COALESCE(pv.code, p.code) as code,
COALESCE(pv.in_store, p.in_store) product_in_store, wq.quantity warehouse_in_store, oq.quantity orders_in_store');
return $qb;
}
public function renderPosition($values)
{
$qb = sqlQueryBuilder()
->select('*')
->from('warehouse_products', 'wp')
->join('wp', 'warehouse_positions', 'wpos', 'wp.id_position=wpos.id')
->where('(wp.id_product=:id_product AND (wp.id_variation IS NULL OR wp.id_variation=:id_variation))')
->setParameters([
'id_product' => $values['id_product'],
'id_variation' => $values['id_variation'],
])
->execute();
$pos = HTML::create('span');
foreach ($qb as $position) {
$pos->tag('a')
->attr('href', "javascript:nw('positions', '".$position['id']."');")
->text($position['code'])->end()
->tag('span')
->text(' , ')
->end();
}
return $pos->end();
}
public function handleFixStore()
{
$affected = 0;
sqlGetConnection()->transactional(function () use (&$affected) {
$storeItemWorker = ServiceContainer::getService(StoreItemWorker::class);
foreach ($storeItemWorker->getMissingProductsQuery(getVal('showNotInStore'))
->select('p.id as id_product', 'pv.id as id_variation', 'COALESCE(pv.in_store, p.in_store) + COALESCE(oq.quantity, 0) - COALESCE(wq.quantity, 0) as missing')->execute() as $row) {
sqlQueryBuilder()
->update('products')
->set('in_store', 'in_store + :quantity')
->setParameter('quantity', -$row['missing'])
->where(Operator::equals(['id' => $row['id_product']]))
->execute();
if ($row['id_variation']) {
sqlQueryBuilder()
->update('products_variations')
->set('in_store', 'in_store + :quantity')
->setParameter('quantity', -$row['missing'])
->where(Operator::equals(['id' => $row['id_variation']]))
->execute();
}
$affected++;
}
});
addActivityLog(ActivityLog::SEVERITY_SUCCESS, ActivityLog::TYPE_CHANGE,
"Oprava skladu - opraveno {$affected} položek ve skladu", tags: [WarehouseBundle::LOG_TAG_WAREHOUSE]);
$this->returnOK("Opraveno {$affected} položek ve skladu");
}
}
return WarehouseMissingProductsList::class;

View File

@@ -0,0 +1,99 @@
<?php
use KupShop\AdminBundle\Util\OrdersListFilter;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\WarehouseBundle\Entity\StoreItem;
use KupShop\WarehouseBundle\Util\StoreItemWorker;
use Query\Operator;
class WarehouseOrdersList extends \KupShop\AdminBundle\AdminList\BaseList
{
protected bool $useLazyNumberOfPages = true;
protected $tableDef = [
'id' => 'id_order',
'fields' => [
'Objednávka' => ['field' => 'order_no', 'type' => 'orders'],
'Přepravka' => ['field' => 'code', 'type' => 'positions', 'type_id' => 'id_position'],
'Stav vychystání' => ['field' => 'state', 'render' => 'renderState'],
'Datum zahájení' => ['field' => 'date_start', 'render' => 'renderDateTime'],
'Datum ukončení' => ['field' => 'date_finish', 'render' => 'renderDateTime'],
'Datum kontroly' => ['field' => 'date_close', 'render' => 'renderDateTime'],
'Akce' => ['field' => 'code', 'render' => 'renderAction'],
],
];
public function renderState($values, $column)
{
if (!$values['state']) {
return '-';
}
$state = json_decode_strict($values['state'], true);
$checkouts = count($state['checkouts'] ?? []);
return "Vychystáno {$checkouts} položek";
}
public function renderAction($values, $column)
{
$actions = [];
if ($values['id_position']) {
$actions[] = \KupShop\KupShopBundle\Util\HtmlBuilder\HTML::create('a')
->attr('title', 'Vysypat produkty z přepravky na pozici VRATKY a odebrat přepravku od objednávky. Bude možné objednávku znovu vychystat do jiného boxu.')
->class('confirm')
->attr('href', "?s=list.php&type=WarehouseOrders&acn=removeBox&ID={$values['id']}")
->text('Zrušit vychystávání');
}
return $actions;
}
public function handleRemoveBox()
{
$warehouse_order_id = $this->getID();
$warehouse_order = sqlQueryBuilder()
->select('id_position', 'id_order')
->from('warehouse_orders')
->where(Operator::equals(['id' => $warehouse_order_id]))
->execute()
->fetch();
sqlGetConnection()->transactional(function () use ($warehouse_order) {
$logger = ServiceContainer::getService(\KupShop\WarehouseBundle\Util\Logger::class);
$logger->activateOrder($warehouse_order['id_order'], function () use ($warehouse_order) {
$worker = ServiceContainer::getService(StoreItemWorker::class);
$items = $worker->getItemsFromOrdersBox($warehouse_order['id_order']);
$id_target_position = $worker->getPositionIdByCode('VRATKY');
foreach ($items as $item) {
$worker->moveBetweenPositions(new StoreItem($item), $item['pieces'], $item['id_position'], $id_target_position);
}
// Detach order from box
sqlQuery('DELETE FROM warehouse_orders WHERE id_order=:id_order', ['id_order' => $warehouse_order['id_order']]);
});
});
$this->returnOK('Vychystané produkty z objednávky jsou na pozici VRATKY a objednávku je možné znovu vychystat di jiné přepravky.');
}
public function getQuery()
{
$qb = sqlQueryBuilder()
->select('wo.*', 'o.order_no', 'wpos.code')
->from('warehouse_orders', 'wo')
->join('wo', 'orders', 'o', 'o.id = wo.id_order')
->leftJoin('wo', 'warehouse_positions', 'wpos', 'wpos.id = wo.id_position');
$qb = ServiceContainer::getService(OrdersListFilter::class)->addQueryBuilderParams($qb, $_GET);
if (getVal('has_box')) {
$qb->andWhere('wo.id_position IS NOT NULL');
}
return $qb;
}
}

View File

@@ -0,0 +1,167 @@
<?php
namespace KupShop\WarehouseBundle\Admin\lists;
use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\KupShopBundle\Util\ArrayUtil;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
use Query\Operator;
class WarehouseProductsList extends BaseList
{
protected $tableDef = [
'id' => 'id',
'fields' => [
'Produkt' => ['field' => 'p_title', 'size' => 2, 'type' => 'product', 'type_id' => 'id_product'],
'Varianta' => ['field' => 'pv_title', 'size' => 1],
'EAN' => ['field' => 'ean'],
'Kód' => ['field' => 'code'],
'Pozice' => ['field' => 'pos_title', 'type' => 'positions', 'type_id' => 'id_position', 'visible' => 'N'],
'Počet kusů' => ['field' => 'pieces', 'size' => 0.5],
'Inventura' => ['field' => 'inventory_date', 'size' => 1, 'render' => 'renderDate'],
'date_stock_in' => ['translate' => true, 'field' => 'date_stock_in', 'spec' => 'p.date_stock_in', 'render' => 'renderDate', 'visible' => 'N', 'fieldType' => BaseList::TYPE_DATE],
'Nadzásoba' => ['field' => 'over_supply', 'render' => 'renderOverSupply'],
'Smazat' => ['field' => '', 'size' => 0.5, 'render' => 'renderDelete'],
],
];
protected $template = 'list/warehouse_products.tpl';
public function renderDelete($values)
{
$url = $this->getListUrl().'&delete[id_product]='.$values['id_product'].'&delete[id_variation]='.$values['id_variation'].'&delete[id_position]='.$values['id_position'];
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$url .= "&delete[id_product_batch]={$values['id_product_batch']}";
}
return findRight('WAR_PROD_DEL') ? HTML::create('a')
->attr('href', $url)
->attr('onclick', 'return confirm(\'Opravdu smazat?\')')
->tag('i')
->class('glyphicon glyphicon-remove')
->end() : null;
}
public function customizeTableDef($tableDef)
{
$tableDef = parent::customizeTableDef($tableDef);
if (getVal('products')) {
$tableDef['fields']['Pozice']['visible'] = 'Y';
$tableDef['fields']['EAN']['visible'] = 'N';
$tableDef['fields']['Kód']['visible'] = 'N';
}
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$fields = $tableDef['fields'];
ArrayUtil::insertIntoArray($fields, 'Smazat', [
'Šarže' => [
'field' => 'batch',
'render' => 'renderBatch',
'spec' => function ($qb) {
$qb->addSelect('pb.code as batch, pb.id as id_batch, pb.date_expiry')
->leftJoin('wp', 'products_batches', 'pb', 'pb.id=wp.id_product_batch');
}, ],
]);
$tableDef['fields'] = $fields;
}
return $tableDef;
}
public function renderOverSupply($values)
{
$ret = HTML::create('input')
->attr('type', 'checkbox')
->attr('name', 'over_supply')
->attr('value', 1)
->attr('data-id-position', $values['id_position'])
->attr('data-id-product', $values['id_product'])
->attr('data-id-variation', $values['id_variation']);
if ($values['over_supply'] == 'Y') {
$ret->attr('checked', 1);
}
return $ret;
}
public function renderBatch($values)
{
return HTML::create('a')
->attr('href', "javascript:nw('productsBatches', '{$values['id_batch']}')")
->text($values['batch'].' ('.$this->renderDate($values, ['field' => 'date_expiry']).')');
}
private function getListUrl()
{
$url = '?s=list.php&type=warehouseProducts';
if (getVal('location')) {
$url .= '&location='.getVal('location');
}
return $url;
}
public function getQuery()
{
if ($delete = getVal('delete')) {
if (!empty($delete['id_product']) && !empty($delete['id_position'])) {
if (empty($delete['id_variation'])) {
$delete['id_variation'] = null;
}
if (findModule(\Modules::PRODUCTS_BATCHES)) {
if (empty($delete['id_product_batch'])) {
$delete['id_product_batch'] = null;
}
}
$info = sqlQueryBuilder()
->select('wpos.code as position, p.title, wp.pieces as pieces')
->from('warehouse_products', 'wp')
->leftJoin('wp', 'warehouse_positions', 'wpos', 'wpos.id = wp.id_position')
->leftJoin('wp', 'products', 'p', 'p.id = wp.id_product')
->where(Operator::equalsNullable($delete))
->execute()->fetch();
if ($info) {
writeDownActivity('Smazán produkt z pozice: '.$info['title'].' na pozici '.$info['position'].
' - celkem '.$info['pieces'].' ks');
$this->deleteSQL('warehouse_products', $delete);
}
redirect($this->getListUrl());
}
}
$qb = sqlQueryBuilder()
->select('wp.id_product, wp.id_variation, wp.id_position, wpos.code as pos_title, p.title as p_title, pv.title as pv_title,
COALESCE(pv.ean, p.ean) as ean, COALESCE(pv.code, p.code) as code, wp.pieces, wp.over_supply, wp.inventory_date')
->from('warehouse_products', 'wp')
->leftJoin('wp', 'warehouse_positions', 'wpos', 'wpos.id=wp.id_position')
->leftJoin('wp', 'products', 'p', 'p.id=wp.id_product')
->leftJoin('wp', 'products_variations', 'pv', 'pv.id=wp.id_variation');
if (getVal('location')) {
$qb->andWhere(\Query\Operator::like(['wpos.id' => getVal('location')]));
}
if (findModule(\Modules::PRODUCTS_BATCHES)) {
$qb->addSelect('wp.id_product_batch');
}
if (getVal('id_product')) {
if (getVal('id_variation', null, 'ne') === 'ne') {
$qb->andWhere(\Query\Operator::equalsNullable(['wp.id_product' => getVal('id_product')]));
} else {
$qb->andWhere(\Query\Operator::equalsNullable(['wp.id_product' => getVal('id_product'), 'wp.id_variation' => getVal('id_variation')]));
}
}
return $qb;
}
}
return WarehouseProductsList::class;

View File

@@ -0,0 +1,119 @@
<?php
$main_class = 'Locations';
class Locations extends Window
{
protected $tableName = 'warehouse_locations';
protected $nameField = 'code';
public function handleUpdate()
{
$update = parent::handleUpdate();
$acn = $this->getAction();
if ($acn == 'add') {
$data = $this->getData();
$this->generatePositions($data['code'], [
$this->emptyToNull($data['A']),
$this->emptyToNull($data['B']),
$this->emptyToNull($data['C']),
$this->emptyToNull($data['D']),
]);
}
return $update;
}
private function generatePositions($code, $maxNumbers)
{
$numbers = [];
foreach ($maxNumbers as $maxNumber) {
if ($maxNumber === null) {
break;
}
$numbers[] = $this->getAllNumbers($maxNumber);
}
$combinations = $this->generateCombinations($numbers);
foreach ($combinations as $combination) {
array_unshift($combination, $code);
$this->insertSQL('warehouse_positions',
[
'code' => join('-', $combination),
'id_location' => $this->getID(),
]);
}
}
private function generateCombinations(array $data, array &$all = [], array $group = [], $value = null, $i = 0)
{
$keys = array_keys($data);
if (isset($value) === true) {
array_push($group, $value);
}
if ($i >= count($data)) {
array_push($all, $group);
} else {
$currentKey = $keys[$i];
$currentElement = $data[$currentKey];
foreach ($currentElement as $val) {
$this->generateCombinations($data, $all, $group, $val, $i + 1);
}
}
return $all;
}
private function getAllNumbers($max)
{
$return = [];
for ($i = 1; $i <= $max; $i++) {
$return[] = $i;
}
return $return;
}
private function emptyToNull($value)
{
return empty($value) ? null : $value;
}
public function handleDelete()
{
if (!findRight('LOC_DEL')) {
$this->returnError('Na mazání nemáte práva');
}
if (sqlQueryBuilder()
->select('*')
->from('warehouse_products', 'wp')
->leftJoin('wp', 'warehouse_positions', 'wpos', 'wpos.id=wp.id_position')
->leftJoin('wpos', 'warehouse_locations', 'wl', 'wl.id=wpos.id_location')
->where(\Query\Operator::equals(['wl.id' => $this->getID()]))
->execute()
->fetchAll()) {
$this->returnError('V lokaci jsou pozice s produkty! Nejprve je nutne preskladnit produkty ze vsech pozici teto lokace!');
}
parent::handleDelete();
}
public function hasRights($name = null)
{
switch ($name) {
case Window::RIGHT_DELETE:
return findRight('LOC_DEL');
case Window::RIGHT_DUPLICATE:
return false;
default:
return parent::hasRights($name);
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
class PositionsMenu extends Menu
{
public function get_vars()
{
$vars = parent::get_vars();
$vars['locations'] = sqlFetchAll(sqlQuery('SELECT id, code FROM warehouse_locations ORDER BY code'), ['id' => 'code']);
return $vars;
}
}

View File

@@ -0,0 +1,13 @@
<?php
class WarehouseLogMenu extends Menu
{
public function get_vars()
{
$vars = parent::get_vars();
$vars['locations'] = sqlFetchAll(sqlQuery('SELECT id, code FROM warehouse_locations ORDER BY code'), ['id' => 'code']);
return $vars;
}
}

View File

@@ -0,0 +1,128 @@
<?php
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\WarehouseBundle\Entity\StoreItem;
$main_class = 'Positions';
class Positions extends Window
{
protected $tableName = 'warehouse_positions';
protected $nameField = 'code';
public function get_vars()
{
$vars = parent::get_vars();
$vars['body']['locations'] = sqlQueryBuilder()->select('*')->from('warehouse_locations')->execute()->fetchAll();
if (findModule(Modules::STORES)) {
$vars['body']['store_transfer'] = sqlQueryBuilder()->select('*')->from('stores_transfers')->andWhere(\Query\Operator::equals(['id_position' => $this->getID()]))->execute()->fetch();
}
$vars['body']['order'] = sqlQueryBuilder()->select('o.order_no as order_no, o.id')->from('warehouse_orders', 'wo')
->join('wo', 'orders', 'o', 'o.id = wo.id_order')
->andWhere(\Query\Operator::equals(['id_position' => $this->getID()]))->execute()->fetch();
return $vars;
}
public function getData()
{
$data = parent::getData();
$acn = $this->getAction();
if ($acn == 'add' && getVal('Submit')) {
$locationName = sqlQueryBuilder()->select('code')->from('warehouse_locations')->where(\Query\Operator::equals(['id' => $data['id_location']]))->execute()->fetchColumn();
$data['code'] = $locationName.'-'.$data['code'];
}
return $data;
}
public function handleDelete()
{
if (!findRight('LOC_DEL')) {
$this->returnError('Na mazání nemáte práva');
}
if (sqlQueryBuilder()->select('*')->from('warehouse_products')
->where(\Query\Operator::equals(['id_position' => $this->getID()]))
->andWhere('pieces > 0')
->execute()->fetchAll()) {
$this->returnError('Nelze! Na pozici jsou produkty!');
}
parent::handleDelete();
}
public function handlePrint()
{
$qb = sqlQueryBuilder()->select('*')
->from('warehouse_positions', 'wp');
$positions = getVal('positions');
if ($positions) {
$qb->andWhere(\Query\Operator::inIntArray($positions, 'id'));
}
$location = getVal('location');
if ($location) {
$qb->andWhere(\Query\Operator::equals(['id_location' => $location]));
}
$smarty = createSmarty(true, true);
$smarty->assign([
'positions' => $qb->execute(),
]);
$smarty->display('print/position.tpl');
exit;
}
public function handleEditProducts()
{
if (!findRight('WAR_PROD_EDIT')) {
$this->returnError('! Na takový úkon nemáte právo !');
}
$data = parent::getData();
$ID = $this->getID();
if (empty($data['id_product']) || empty($data['pieces'])) {
$this->returnError('Chybí vybraný produkt nebo kusy!');
}
sqlGetConnection()->transactional(function () use ($ID, $data) {
$storeItemWorker = ServiceContainer::getService(\KupShop\WarehouseBundle\Util\StoreItemWorker::class);
if (empty($data['id_variation'])) {
$data['id_variation'] = null;
}
if (empty($data['id_product_batch'])) {
$data['id_product_batch'] = null;
}
$storeItemWorker->createStoreItem(new StoreItem($data), $data['pieces'], $ID, ['handle_added' => true], true);
});
$this->returnOK('Přidáno. A zalogováno !!');
}
public function hasRights($name = null)
{
switch ($name) {
case Window::RIGHT_DELETE:
return findRight('LOC_DEL');
case Window::RIGHT_DUPLICATE:
case Window::RIGHT_SAVE:
if ($this->getAction() == 'add') {
return parent::hasRights($name);
}
return false;
default:
return parent::hasRights($name);
}
}
}

View File

@@ -0,0 +1,110 @@
{if $result.positions}
<div class="form-group form-group-flex">
<div class="col-md-4 control-label">
<label>Na pozici</label>
</div>
<div class="col-md-6">
<select class="selecter" name="config[selectedPosition]" id="positionSelect">
<option value="-1" selected>
Všechny pozice
</option>
{foreach $result.positions as $id => $position}
<option value="{$id}">
{$position.code}
</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group form-group-flex">
<div class="col-md-4 control-label">
<label>Vytvořit z varianty</label>
</div>
<div class="col-md-6">
<select class="selecter" name="config[selectedBatch]" id="batchSelect">
{foreach $batches as $id => $batch}
<option value="{$id}">
{$batch.title}: {$batch.code} ({$batch.date_expiry|date_format:"d.m.Y"})
</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group form-group-flex">
<div class="col-md-4 control-label">
<label>Bude přesunuto kusů: </label>
</div>
<div class="col-md-6" id="piecesToMove">
-
</div>
</div>
{if !$productExists}
<div class="form-group form-group-flex">
<div class="col-md-4 control-label">
<label>Přidat text před název:</label>
</div>
<div class="col-md-6">
<input type="text" class="form-control input-sm" name="config[prependText]" value="{t}Zachraňte! {/t}">
</div>
</div>
{/if}
{include 'actions/createExpiringProduct_discount.tpl'}
<div class="form-group form-group-flex" style="display: none;" id="batchExists">
<div class="alert alert-success col-md-10 col-md-offset-1"><span class="glyphicon glyphicon-exclamation-sign"></span>
Expirující produkt/varianta s tímto DMT již existuje. Šarže bude přiřazena k již existujícímu produktu/variantě se stejným DMT.
</div>
</div>
<hr>
<script>
var result = JSON.parse('{$result|json_encode nofilter}');
$positionSelect = $('#positionSelect');
$batchSelect = $('#batchSelect');
$positionSelect.on("change", function () {
const selectedId = parseInt($(this).val());
$batchSelect.empty();
const variations = (result.positions[selectedId]) ? result.positions[selectedId].variations : result.allVariations;
Object.values(variations).forEach((val) => {
$batchSelect.append($("<option></option>").attr("value", (val.id) ? val.id : -1).text(val.title))
});
$batchSelect.trigger('change');
});
$batchSelect.on("change", function () {
let selectedId = $(this).val();
selectedId = selectedId > 0 ? parseInt(selectedId): '';
const selectedPosition = parseInt($('#positionSelect').val());
try {
var variation = (selectedPosition > 0) ? result.positions[selectedPosition].variations[selectedId ?? ''] : result.allVariations[selectedId ?? ''];
} catch (e) {
}
$('#piecesToMove').html(variation.availablePieces ?? '-');
$('#priceDiscountDropdown [data-vat]').val(variation.vat ?? 0);
$('#priceDiscountDropdown [data-price-original]').val(variation.price).trigger('change');
if (variation.alreadyExists ?? false) {
$('#batchExists').show();
$('#batchDiscount').hide();
} else {
$('#batchExists').hide();
$('#batchDiscount').show();
}
});
$positionSelect.trigger('change');
$batchSelect.trigger('change');
</script>
{else}
<input required hidden>
<p class="text-center m-b-3"><strong>Nelze provést, produkt nemá žádné kusy k rozdělení.</strong></p>
{/if}

View File

@@ -0,0 +1,26 @@
{extends "list.tpl"}
{block addFilter}
<div class="checkbox">
<input id="not_in_store" name="showNotInStore" type="checkbox" class="checkbox" value="1" {if $showNotInStore}checked{/if}>
<label for="not_in_store">zobrazit produkty, které nejsou ve skladu</label>
</div>
<script type="text/javascript">
$("#not_in_store").on("change", function (e) {
$this = $(this);
var notInStore = 0;
if ($this.is(':checked')) {
notInStore = 1;
}
window.location.href = window.location.href + "&showNotInStore=" + notInStore;
});
</script>
{/block}
{block buttons prepend}
<a class="btn btn-default confirm"
href="launch.php?s=list.php&type=WarehouseMissingProducts&acn=fixStore&showNotInStore={$showNotInStore}"
title="opravit sklad? Aktuální počty kusů u produktů budou upraveny tak, aby seděly se skladem. Chcete pokračovat">Opravit sklad</a>
{/block}

View File

@@ -0,0 +1,14 @@
{extends file="list.tpl"}
{block buttons prepend}
<button data-print class="btn btn-primary">Vytisknout štítky</button>
<script>
$('[data-print]').click(function(){
$.redirectPost({
url: 'launch.php?s=printCenter.php&type=warehouse_positions',
data: $(':checkbox').serializeArray()
});
});
</script>
{/block}

View File

@@ -0,0 +1,31 @@
{extends file="list.tpl"}
{block addFilter}
<script>
$(document).ready(function() {
$('input[name=over_supply]').click(function () {
const id_position = $(this).data('id-position');
const id_product = $(this).data('id-product');
const id_variation = $(this).data('id-variation');
var value;
if ($(this).is(':checked')) {
value = 'Y';
} else {
value = 'N';
}
console.log(id_position, id_product, id_variation, value);
$.ajax({
method: "GET",
url: "./launch.php?s=ajax.php&type=positionsOverSupply",
data: { id_position: id_position, id_product: id_product, id_variation: id_variation, value: value }
}).done(function( data ) {
showInfoMessage('Uloženo', 'success');
});
});
});
</script>
{/block}

View File

@@ -0,0 +1,40 @@
{extends file="menu.tpl"}
{block name="menu-items"}
<li class="nav-header"><i class="glyphicon glyphicon-tags"></i><span>{translate_type type=$type}</li>
<li class="nav-header smaller"><i class="glyphicon glyphicon-search"></i><span>{'search'|translate}</span></li>
<li class="pill-content">
<ul class="nav-sub nav-pills">
<form id='search' target="mainFrame" method="get" action="launch.php" class="form-inline">
<input type="hidden" name="type" value="warehouseInventory" />
<input type="hidden" name="s" value="list.php">
<div class="form-group">
<input type="text" class="form-control input-sm" name="id_product" maxlength="100" value="" onKeyPress="checkInputData('int')" placeholder="{'searchNameCode'|translate:'orders'}"/>
</div>
<div class="form-group">
<select name="id_variation" class="input-sm form-control"></select>
<script type="text/javascript">
$(function(){
initAutocompleteVariation('[name=id_product]', '[name=id_variation]');
});
</script>
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="dateFrom" id="dateFrom" maxlength="10" value="" placeholder="Od" autocomplete="off"/>
{insert_calendar selector='#dateFrom' format='datetime'}
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="dateTo" id="dateTo" maxlength="10" value="" placeholder="Do" autocomplete="off"/>
{insert_calendar selector='#dateTo' format='datetime'}
</div>
{block name="custom-menu-items"}{/block}
<div class="form-group">
<input type="reset" value="{'delete'|translate:'orders'}" class="btn btn-danger btn-sm"/>
<input type="submit" value="{'searchBtn'|translate:'orders'}" class="btn btn-primary btn-sm"/>
</div>
</form>
</ul>
</li>
{/block}

View File

@@ -0,0 +1,102 @@
{extends file="menu.tpl"}
{block name="menu-items"}
<li class="nav-header"><i class="glyphicon glyphicon-tags"></i><span>{translate_type type=$type}</span></li>
<li class="nav-header smaller"><i class="glyphicon glyphicon-search"></i><span>{'search'|translate}</span></li>
<li class="pill-content">
<ul class="nav-sub nav-pills">
<form id='search' target="mainFrame" method="get" action="launch.php" class="form-inline">
<input type="hidden" name="type" value="warehouseLog" /><input type="hidden" name="s" value="list.php">
<div class="form-group">
<div class="input-group">
{print_select name="location" var=["" => "Vyberte lokaci"] + $locations}
<span class="input-group-btn">
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control input-sm" name="search" maxlength="20" value="" placeholder="{'searchCode'|translate}"/>
<span class="input-group-btn">
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control input-sm" name="order" maxlength="20" value="" placeholder="Kód objednávky"/>
<span class="input-group-btn">
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="id_product" maxlength="100" value="" onKeyPress="checkInputData('int')" placeholder="{'searchNameCode'|translate:'orders'}" data-autocomplete-params="visible"/>
</div>
<div class="form-group">
<select name="id_variation" class="input-sm form-control"></select>
<script type="text/javascript">
$(function(){
initAutocompleteVariation('[name=id_product]', '[name=id_variation]');
});
</script>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" class="input" name="onlyManuallyEdited" value="1" id="onlyManuallyEdited">
<label for="onlyManuallyEdited">{'onlyManuallyEdited'|translate}</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" class="input" name="onlyReplacements" value="1" id="onlyReplacements">
<label for="onlyReplacements">{'onlyReplacements'|translate}</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" class="input" name="onlyStockIn" value="1" id="onlyStockIn">
<label for="onlyStockIn">{'onlyStockIn'|translate}</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" class="input" name="onlyCreation" value="1" id="onlyCreation">
<label for="onlyCreation">{'onlyCreation'|translate}</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" class="input" name="onlyDeletion" value="1" id="onlyDeletion">
<label for="onlyDeletion">{'onlyDeletion'|translate}</label>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="dateFrom" id="dateFrom" maxlength="10" value="" placeholder="Od" autocomplete="off"/>
{insert_calendar selector='#dateFrom' format='datetime'}
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="dateTo" id="dateTo" maxlength="10" value="" placeholder="Do" autocomplete="off"/>
{insert_calendar selector='#dateTo' format='datetime'}
</div>
<div class="form-group">
<input type="reset" value="{'delete'|translate:'orders'}" class="btn btn-danger btn-sm"/>
<input type="submit" value="{'searchBtn'|translate:'orders'}" class="btn btn-primary btn-sm"/>
</div>
</form>
</ul>
</li>
{/block}

View File

@@ -0,0 +1,48 @@
{extends file="menu.tpl"}
{block name="menu-items" append}
<li class="nav-header smaller"><i class="glyphicon glyphicon-search"></i><span>{'search'|translate}</span></li>
<li class="pill-content">
<ul class="nav-sub nav-pills">
<form id='search' target="mainFrame" method="get" action="launch.php" class="form-inline">
<input type="hidden" name="type" value="positions" /><input type="hidden" name="s" value="list.php">
<div class="form-group">
<div class="input-group">
{print_select name="location" var=["" => "Vyberte lokaci"] + $locations}
<span class="input-group-btn">
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control input-sm" name="search" maxlength="20" value="" placeholder="{'searchCode'|translate}"/>
<span class="input-group-btn">
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control input-sm" name="id_product" maxlength="100" value="" onKeyPress="checkInputData('int')" placeholder="{'searchNameCode'|translate:'orders'}"/>
</div>
<div class="form-group">
<select name="id_variation" class="input-sm form-control"></select>
<script type="text/javascript">
$(function(){
initAutocompleteVariation('[name=id_product]', '[name=id_variation]');
});
</script>
</div>
<div class="form-group">
<input type="reset" value="{'delete'|translate:'orders'}" class="btn btn-danger btn-sm"/>
<input type="submit" value="{'searchBtn'|translate:'orders'}" class="btn btn-primary btn-sm"/>
</div>
</form>
</ul>
</li>
{/block}

View File

@@ -0,0 +1,120 @@
<style type="text/css" media="all">
@media print {
@page {
margin: 0 !important;
padding: 0;
}
.stitek {
float: none !important;
border: none !important;
}
}
body {
margin: 0;
padding: 0;
color: black;
}
.stitek {
width: 68mm;
height: 38mm;
box-sizing: border-box;
page-break-after: always;
border: dimgrey dashed 1px;
overflow: hidden;
position: relative;
background-color: white;
}
.stitek > div {
display: table-cell;
}
.barcode {
position: absolute;
left: 0;
bottom: 3mm;
width: 100%;
height: 17mm;
text-align: center;
}
.barcode.up {
bottom: unset;
top: 3mm;
}
.barcode svg {
max-width: 90%;
}
.code {
font-family: monospace;
font-size: 28px;
font-weight: bold;
text-align: center;
position: absolute;
left: 10mm;
top: 5mm;
width: 48mm;
height: 15mm;
line-height: 25px;
}
.code.down {
top: unset;
bottom: 0mm;
}
.arrow.down {
top: unset;
bottom: 6mm;
}
.arrow img.down {
transform: rotate(180deg);
}
.arrow {
position: absolute;
left: 0;
top: 0mm;
width: 15mm;
height: 10mm;
}
.arrow.right {
left: unset;
right: 0;
}
</style>
{if !$positionsArrow}
{$positionsArrow = 'up'}
{/if}
{if !$positionsBarcode}
{$positionsBarcode = 'down'}
{/if}
{$arrowUrl = '/web/bundles/warehouse/images/arow_up.svg'}
{$arrowClass = $positionsArrow}
{foreach $positions as $position}
{block stitek}
<div class="stitek">
<div class="arrow left {if $positionsBarcode == 'up'}down{/if}">
<img src="{$arrowUrl}" class="{$arrowClass}">
</div>
<div class="code {if $positionsBarcode == 'up'}down{/if}">
{$position.code}
</div>
<div class="arrow right {if $positionsBarcode == 'up'}down{/if}">
<img src="{$arrowUrl}" class="{$arrowClass}">
</div>
{block barcode}
<div class="barcode {if $positionsBarcode == 'up'} up {/if}">
{insert_barcode code=$position.code height=70 width=2}
</div>
{/block}
</div>
{/block}
{/foreach}

View File

@@ -0,0 +1,70 @@
<style>
.code {
display: inline-block;
width: auto;
text-align: center;
margin: 10px;
}
hr {
border-color: black;
}
table tr {
border-bottom: black dashed 1px;
}
</style>
<h1>Objednávka {$order.order_no} {insert_barcode code=$order.order_no height=50}</h1>
Boxy:
{get_positions assign='box_positions' id_location=2 only_empty=true}
{foreach $box_positions as $position}
<div class="code">
{insert_barcode code=$position.code height=30}<br>{$position.code}
</div>
{/foreach}
<hr>
<table width="100%" border="0" cellpadding="3" cellspacing="0" class="pieces">
<tr>
<th align="center" width="5%">{'quantity'|translate:'printOrder'}</th>
<th align="left">{'description'|translate:'printOrder'}</th>
<th align="center">ean</th>
<th align="center">pozice</th>
</tr>
{foreach $order.items as $item}
<tr {if $item@last}class="last"{/if}>
<td>{$item.pieces}</td>
<td>{$item.descr}</td>
<td>
{if $item.ean}
<div class="code">
{insert_barcode type='EAN13' code=$item.ean height=30}<br>{$item.ean}
</div>
{else}
Nemá EAN!!!
{/if}
</td>
<td>
{get_positions assign='item_positions' id_item=$item.id}
{foreach $item_positions as $position}
<div class="code">
{insert_barcode code=$position.code height=30}<br>{$position.code}
</div>
{/foreach}
</td>
</tr>
{/foreach}
</table>
<hr>
Pár pozic:
{get_positions assign='box_positions'}
{foreach $box_positions as $position}
<div class="code">
{insert_barcode code=$position.code height=30}<br>{$position.code}
</div>
{/foreach}

View File

@@ -0,0 +1,181 @@
{extends file="[shared]/window.tpl"}
{block title}
Pozice {$body.data.code}
{/block}
{block size}
{if $body.acn =="edit"}
width = 1220;
height = 900;
{/if}
{/block}
{block tabs}
{windowTab id='flapLocations' label="Lokace {$body.data.code}"}
{/block}
{block tabsContent}
<div id="flapLocations" class="tab-pane fade boxStatic">
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'flapLocationsLabel'|translate}</label>
</div>
<div class="col-md-5">
<input type="text" data-location="location" {if $body.acn == 'edit'}disabled{/if} class="form-control input-sm" name="data[code]" maxlength="20" value="{$body.data.code}" placeholder="A1, A2, ..."/>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label ">
<label>Mazat prázdné pozice</label>
<a class="help-tip" data-toggle="tooltip" title="Pokud počet kusů produktu na pozici klesne na nulu, smaže se přiřazení produktu k dané pozici."><i class="bi bi-question-circle"></i></a>
</div>
<div class="col-md-1">
{print_toggle name="delete_empty" value=$body.data.delete_empty}
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'scoreMultiplier'|translate}</label>
</div>
<div class="col-md-1">
<input type="text" class="form-control input-sm" name="data[score_multiplier]" value="{$body.data.score_multiplier|default:1}"/>
</div>
</div>
{if $body.acn == 'add'}
<div class="form-group">
<div class="col-md-2 control-label">
<label>A</label>
</div>
<div class="col-md-1">
<input type="text" class="form-control input-sm" data-location="A" name="data[A]" maxlength="20" value="{$body.data.code}"/>
</div>
<div class="col-md-1 control-label">
<label>B</label>
</div>
<div class="col-md-1">
<input type="text" class="form-control input-sm" data-location="B" name="data[B]" maxlength="20" value="{$body.data.code}"/>
</div>
<div class="col-md-1 control-label">
<label>C</label>
</div>
<div class="col-md-1">
<input type="text" class="form-control input-sm" data-location="C" name="data[C]" maxlength="20" value="{$body.data.code}"/>
</div>
<div class="col-md-1 control-label">
<label>D</label>
</div>
<div class="col-md-1">
<input type="text" class="form-control input-sm" data-location="D" name="data[D]" maxlength="20" value="{$body.data.code}"/>
</div>
</div>
<div class="form-group">
<div class="col-md-3 control-label">
<strong>Pozice se generují ve formátu:</strong>
</div>
<div class="col-md-3">
<input type="text" class="form-control input-sm" name="data[format]"
value="{literal}{Lokace}{A}-{B}-{C}-{D}{/literal}" disabled/>
</div>
</div>
<div id="positions-example" style="margin-top: 50px">
<div style="border-bottom: 1px solid rgba(0, 0, 0, 0.1);font-weight: bold;margin-bottom: 20px">Po uložení budou vygenerované pozice</div>
<div class="row">
<div class="col-md-2">
<strong>První pozice</strong>
</div>
<div class="col-md-2" data-first-position></div>
</div>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-2">.</div>
</div>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-2">.</div>
</div>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-2">.</div>
</div>
<div class="row">
<div class="col-md-2">
<strong>Poslední pozice</strong>
</div>
<div class="col-md-2" data-last-position></div>
</div>
</div>
<script>
var Locations = {
getInputs: function () {
var result = [];
$('[data-location]').each(function () {
var $input = $(this);
result[$input.data('location')] = $input.val();
});
return result;
},
getLastPosition: function () {
var inputs = this.getInputs(),
joinArray = [];
for (var input in inputs) {
if (inputs.hasOwnProperty(input)) {
if (inputs[input] == '') {
break;
}
joinArray.push(inputs[input]);
}
}
return joinArray.join('-');
},
getFirstPosition: function () {
var inputs = this.getInputs(),
joinArray = [];
for (var input in inputs) {
if (inputs.hasOwnProperty(input)) {
if (inputs[input] == '') {
break;
}
if (input == 'location') {
joinArray.push(inputs[input]);
continue;
}
joinArray.push(1);
}
}
return joinArray.join('-');
}
};
$(document).ready(function () {
$('[data-location]').on('input', function () {
$('[data-first-position]').text(Locations.getFirstPosition());
$('[data-last-position]').text(Locations.getLastPosition());
});
});
</script>
{/if}
</div>
{/block}

View File

@@ -0,0 +1,44 @@
{extends "[shared]/windowFrame.tpl"}
{block content}
<div id="flapPosWarehouseSettings" class="tab-pane fade boxStatic">
{if $body.data.data != NULL}
{$data = json_decode($body.data.data, true)}
{/if}
<div class="form-group">
<div class="col-md-2 control-label">
<label>{'virtualBox'|translate:"posSettings"}</label>
</div>
<div class="col-md-8">
<div class="input-group">
<select class="selecter"
data-autocomplete="warehouse_positions"
data-autocomplete-params="id_locations=2"
data-preload="warehouse_positions"
data-placeholder="{'virtualBox'|translate:"posSettings"}"
name="data[data][virtual_box]">
{foreach $data.virtual_box as $box}
<option value="{$box}" selected>{$box}</option>
{/foreach}
</select>
{if !empty($data.virtual_box)}
<span class="input-group-btn">
<a href="/admin/launch.php?s=positions.php&acn=edit&ID={$data.virtual_box}&" title="Nastavení virtuálního boxu" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-home"></span>
</a>
</span>
{/if}
</div>
</div>
</div>
</div>
<script type="application/javascript">
initForm({
selector: '#flapPosWarehouseSettings',
beforeAdd: function (original) {
var $item = original();
window.preloadAutocompletes($item);
},
});
</script>
{/block}

View File

@@ -0,0 +1,109 @@
{extends file="[shared]/window.tpl"}
{block title}
Pozice {$body.data.code}
{/block}
{block size}
{if $body.acn =="edit"}
width = 1220;
height = 900;
{/if}
{/block}
{block tabs}
{windowTab id='flapLocations' label="Pozice {$body.data.code}"}
{/block}
{block tabsContent}
{if $body.acn =="edit"}
{if findRight('WAR_PROD_EDIT')}
{ifmodule PRODUCTS_BATCHES}
{$batches= true}
{/ifmodule}
<div class="form-group">
<div class="col-md-{if $batches}4{else}5{/if}">
<input type="text" class="form-control input-sm" id="id_product" name="data[id_product]" maxlength="100" value=""
onKeyPress="checkInputData('int')" placeholder="{'searchNameCode'|translate:'orders'}"/>
</div>
<div class="col-md-4">
<select id="id_variation" name="data[id_variation]" class="input-sm form-control selecter" data-text-single="Vyberte variantu"></select>
<script type="text/javascript">
$(function () {
initAutocompleteVariation('#id_product', '#id_variation');
});
</script>
</div>
{if $batches}
<div class="col-md-2">
<select id="id_batch" name="data[id_product_batch]" class="input-sm form-control selecter" data-text-single="Vyberte šarži"></select>
<script type="text/javascript">
initAutocompleteBatches('#id_product', '#id_variation', '#id_batch');
</script>
</div>
{/if}
<div class="col-md-{if $batches}1{else}2{/if}">
<input type="text" class="form-control input-sm"
name="data[pieces]" placeholder="ks" size="2" maxlength="255"
onkeypress="checkInputData('int');"/>
</div>
<div class="col-md-1">
<button type="submit" name='acn' value='editProducts' class="btn btn-primary btn-sm confirm"
title="seš si jistej, co děláš? Víš, že se to nemá!">{'editProducts'|translate}</button>
</div>
</div>
{/if}
<div id="flapLocations" class="tab-pane fade boxFlex box iframe-box">
<iframe class='on-demand boxFlex' src="" data-src="launch.php?s=list.php&type=warehouseProducts&location={$body.data.id}"></iframe>
</div>
{else}
<div class="form-group">
<div class="col-md-1 control-label text-left">
<label>Lokace</label>
</div>
<div class="col-md-2 control-label">
<label>{'flapLocationsLabel'|translate}</label>
</div>
</div>
<div id="flapLocations" class="tab-pane fade boxStatic">
<div class="form-group">
<div class="col-md-2 text-right">
<select name="data[id_location]" class="selecter">
{foreach $body.locations as $location}
<option value="{$location.id}">{$location.code}</option>
{/foreach}
</select>
</div>
<div class="col-md-3">
<input type="text" class="form-control input-sm" name="data[code]" maxlength="20" value="" placeholder="police-krabice, např. 02-03"/>
</div>
</div>
</div>
{/if}
<div class="form-group">
<div class="col-md-4">
{insert_barcode height=30 width=2 code=$body.data.code}
</div>
<div class="col-md-4">
{if $body.order.id}
<div class="alert alert-warning">
Box obsahuje objednávku <a href="javascript:nw('order', '{$body.order.id}');">{$body.order.order_no}</a>.
</div>
{/if}
</div>
<div class="col-md-4">
{if $body.store_transfer.id}
<div class="alert alert-warning">
Box obsahuje převodku <a href="javascript:nw('storesTransfers', '{$body.store_transfer.id}');">{$body.store_transfer.id}</a>.
</div>
{/if}
</div>
</div>
{/block}

View File

@@ -0,0 +1,118 @@
<div id="flapWarehouse" class="tab-pane fade in boxFlex">
{printDateBar barName='Warehouse' graphs=['Warehouse']}
<div style="float: right">
</div>
<div class="row bottom-space" id="users_content">
{if $tab.data.userData}
<div class="col-md-6">
<div>
<div class="panel panel-default panel-sm">
<div class="panel-heading">
<a class="help-tip" style="position:relative; float:right; margin-top: 5px;" data-toggle="tooltip"
title="{'userStats'|translate}">
<i class="bi bi-question-circle"></i>
</a>
<h3 class="panel-title">{'userStats'|translate}</h3>
</div>
<table class="table">
<tr>
<th style="10%">{'login'|translate}</th>
<th style="75%">{'count_points'|translate}</th>
</tr>
{foreach $tab.data.userData as $item}
<tr style="background-color: #f9f9f9; border-top: 2px solid #ddd !important; ">
<td>{$item.login}</td>
<td>{$item.score}</td>
</tr>
{/foreach}
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div>
<div class="panel panel-default panel-sm">
<div class="panel-heading">
<h3 class="panel-title">Statistika balení</h3>
</div>
<table class="table">
<tr>
<th style="10%">{'login'|translate}</th>
<th style="75%">Počet balíků</th>
</tr>
{foreach $tab.data.packingData as $item}
<tr style="background-color: #f9f9f9; border-top: 2px solid #ddd !important; ">
<td>{$item.login}</td>
<td>{$item.score}</td>
</tr>
{/foreach}
</table>
</div>
</div>
</div>
{/if}
</div>
<div class="row bottom-space" data-display-area="warehouse">
<div class="col-md-6">
{printIntervalBar barName='Warehouse'}
<div class="well graph" id="warehouse">
</div>
</div>
<div class="col-md-6">
<div class="well graph" id="warehousePacking">
</div>
</div>
</div>
</div>
<script type="application/javascript">
{block onready append}
{literal}
function getWarehouseGraph(interval){
showGraphsArea($(this));
reloadDivWarehouse();
renderWarehouseGraph(interval);
}
function reloadDivWarehouse() {
var from = $(document).find('[name=fromWarehouse]').val(),
to = $(document).find('[name=toWarehouse]').val();
reloadDiv('#users_content', {
type: 'stats',
fromWarehouse: from,
toWarehouse: to,
}, 'editform');
}
function renderWarehouseGraph(interval){
var from = $(document).find('[name=fromWarehouse]').val(),
to = $(document).find('[name=toWarehouse]').val();
reloadGraph('#warehouse', {
typegraph:"warehouse",
acn:'getWarehouseGraph',
fromWarehouse: from,
toWarehouse: to,
intervalWarehouse: interval
},'editform');
reloadGraph('#warehousePacking', {
typegraph:"warehouse",
acn:'getWarehouseGraph',
fromWarehouse: from,
toWarehouse: to,
intervalWarehouse: interval,
packing: true
},'editform');
}
{/literal}
{/block}
</script>

View File

@@ -0,0 +1,49 @@
{extends "[shared]/windowFrame.tpl"}
{block content}
<div id="flapStoresLocations" class="tab-pane fade boxStatic">
<div class="row bottom-space">
<div class="col-md-12">
<h1 class="h4 main-panel-title">{'warehouseLocationAssignment'|translate:'storesLocationsTab'}</h1>
</div>
</div>
<div class="form-group" id="locations">
<div class="col-md-2 control-label">
<label>{'locations'|translate:'storesLocationsTab'}</label>
</div>
<div class="col-md-8">
<select multiple="multiple"
class="selecter selecter-sortable"
data-autocomplete="warehouse_locations"
data-preload="warehouse_locations"
data-placeholder="{'selectLocation'|translate:"storesLocationsTab"}"
name="data[data][warehouse_locations][]">
{foreach $body.data.data.warehouse_locations as $location}
<option value="{$location}" selected>{$location}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label two-lines"><label>{'base_position'|translate}</label></div>
<div class="col-md-6">
{print_select name="data[id_base_position]" var=$body.base_positions selected=$body.data.id_base_position}
</div>
</div>
<div class="form-group">
<div class="col-md-2 control-label"><label>{'return_table_position'|translate}</label></div>
<div class="col-md-6">
{print_select name="data[id_return_table]" var=$body.base_positions selected=$body.data.id_return_table}
</div>
</div>
</div>
<script type="application/javascript">
initForm({
selector: '#locations',
beforeAdd: function (original) {
var $item = original();
window.preloadAutocompletes($item);
},
});
</script>
{/block}

View File

@@ -0,0 +1,77 @@
<div id="flapWarehouse" class="tab-pane fade active in boxStatic box">
<h2 class="h4 main-panel-title">Oprávnění</h2>
<div class="row">
<div class="col-md-2 control-label"><label>{'LOC'|translate:'warehouseAdminsWindowTab'}</label></div>
<div class="col-md-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[LOC]" id="LOC"
value="ON" {if {find_right name="LOC" var=$body.data.privilege}}checked{/if} />
<label for="LOC"> {'LOC'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
<div class="col-md-3">
<div class="checkbox">
<input type="checkbox" class="check" name="data[LOC_DEL]" id="LOC_DEL"
value="ON" {if {find_right name="LOC_DEL" var=$body.data.privilege}}checked{/if} />
<label for="LOC_DEL"> {'LOC_DEL'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_PROD_EDIT]" id="WAR_PROD_EDIT"
value="ON" {if {find_right name="WAR_PROD_EDIT" var=$body.data.privilege}}checked{/if} />
<label for="WAR_PROD_EDIT"> {'WAR_PROD_EDIT'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
<div class="col-md-4">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_PROD_DEL]" id="WAR_PROD_DEL"
value="ON" {if {find_right name="WAR_PROD_DEL" var=$body.data.privilege}}checked{/if} />
<label for="WAR_PROD_DEL"> {'WAR_PROD_DEL'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_ORDER_EDIT]" id="WAR_ORDER_EDIT"
value="ON" {if {find_right name="WAR_ORDER_EDIT" var=$body.data.privilege}}checked{/if} />
<label for="WAR_ORDER_EDIT"> {'WAR_ORDER_EDIT'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_INV]" id="WAR_INV"
value="ON" {if {find_right name="WAR_INV" var=$body.data.privilege}}checked{/if} />
<label for="WAR_INV"> {'WAR_INV'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_EAN]" id="WAR_EAN"
value="ON" {if {find_right name="WAR_EAN" var=$body.data.privilege}}checked{/if} />
<label for="WAR_EAN"> {'WAR_EAN'|translate:'warehouseAdminsWindowTab'}</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-2">
<div class="checkbox">
<input type="checkbox" class="check" name="data[WAR_CHECK_STOCK_IN_CHECK_ALL]" id="WAR_CHECK_STOCK_IN_CHECK_ALL"
value="ON" {if {find_right name="WAR_CHECK_STOCK_IN_CHECK_ALL" var=$body.data.privilege}}checked{/if} />
<label for="WAR_CHECK_STOCK_IN_CHECK_ALL"> {'WAR_CHECK_STOCK_IN_CHECK_ALL'|translate}</label>
</div>
</div>
</div>
<h2 class="h4 main-panel-title">Nastavení čtečky</h2>
<img style="width: 250px;height: 250px;"
src="https://api.qrserver.com/v1/create-qr-code/?size=250x250&margin4&data={$tab.data.settings|escape:'url'}" >
</div>

View File

@@ -0,0 +1,63 @@
<div id="flapWarehouse" class="tab-pane fade active in boxStatic box">
<div class="form-group">
<div class="col-md-2 control-label"><label>{'note_warehouse'|translate:'warehouseOrdersWindowTab'}</label></div>
<div class="col-md-8">
<textarea name="data[custom_data][note_warehouse]" id="note_warehouse" class="form-control input-sm" rows="3" cols="30">{$body.custom_data.note_warehouse}</textarea>
</div>
</div>
{ifmodule PRODUCTS_BATCHES}
<h4 class="main-panel-title">Šarže</h4>
<div class="panel-group panel-group-lists">
<div class="panel">
<div class="panel-heading">
<div class="row row-flex">
<div class="col-md-7">
<label>Produkt</label>
</div>
<div class="col-md-1">
<label>Celkem kusů</label>
</div>
<div class="col-md-2">
<label>Šarže</label>
</div>
<div class="col-md-2">
<label>Počet kusů šarže</label>
</div>
</div>
</div>
</div>
{foreach $tab.data.itemsWithBatches as $item}
<div class="panel" style="background-color: {$item.bg_color};">
<div class="panel-heading">
<div class="row">
<div class="col-md-7">
<p>{$item.descr}</p>
</div>
<div class="col-md-1">
<p>{$item.pieces}</p>
</div>
<div class="col-md-4">
<div class="row">
{foreach $item.batches as $batch}
<div class="col-md-6">
<p style="white-space: nowrap;">
{$batch.code}
</p>
</div>
<div class="col-md-6">
<p style="white-space: nowrap;">
{$batch.pieces}
</p>
</div>
{/foreach}
</div>
</div>
</div>
</div>
</div>
{/foreach}
</div>
{/ifmodule}
</div>