408 lines
14 KiB
PHP
408 lines
14 KiB
PHP
<?php
|
|
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
|
|
use KupShop\OrderingBundle\Event\OrderItemEvent;
|
|
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
|
|
|
/**
|
|
* Class PosCart.
|
|
*/
|
|
class PosCartBase extends Cart
|
|
{
|
|
/** @var \Symfony\Component\HttpFoundation\Session\Session */
|
|
private $session;
|
|
|
|
/**
|
|
* Fetching functions.
|
|
*/
|
|
protected function fetchProducts()
|
|
{
|
|
global $dbcfg, $cfg;
|
|
|
|
$this->session = ServiceContainer::getService('session');
|
|
|
|
$this->totalPriceNoVat = DecimalConstants::zero();
|
|
$this->totalPriceWithVat = DecimalConstants::zero();
|
|
|
|
$where = queryCreate($this->getSelectParams(), true);
|
|
|
|
$fields = 'c.id, c.pieces, c.id_variation, c.note, p.id as id_product, p.title, p.price, p.producer as id_producer, pr.name as producer, p.discount, COALESCE(pv.in_store, p.in_store) as in_store, p.vat,
|
|
FIND_IN_SET("Z", p.campaign)>0 free_shipping, COALESCE(pv.delivery_time, p.delivery_time) as delivery_time, COALESCE(pv.ean, p.ean) as ean ';
|
|
$from = 'cart AS c
|
|
LEFT JOIN products AS p ON c.id_product=p.id
|
|
LEFT JOIN producers AS pr ON pr.id=p.producer
|
|
LEFT JOIN products_variations AS pv ON c.id_variation=pv.id
|
|
LEFT JOIN photos_products_relation ppr ON p.id=ppr.id_product AND ppr.show_in_lead="Y"';
|
|
|
|
if (findModule('products_variations', 'variationCode')) {
|
|
$fields .= ', pv.code as variation_code';
|
|
}
|
|
|
|
$SQL = sqlQuery("SELECT {$fields}
|
|
FROM {$from}
|
|
WHERE {$where}
|
|
GROUP BY c.id
|
|
ORDER BY c.id");
|
|
|
|
foreach ($SQL as $row) {
|
|
$product = new Product($row['id_product']);
|
|
|
|
$product->createFromDB($product->id);
|
|
|
|
$product->cart_item_id = $row['id'];
|
|
|
|
$dec_Pieces = Decimal::fromString("{$row['pieces']}");
|
|
$product->pieces = $row['pieces'];
|
|
|
|
$product->note = $product->parseNote($row['note']);
|
|
$product->inStore = $row['in_store'];
|
|
|
|
$IDvariation = $product->id_variation = $row['id_variation'];
|
|
|
|
if (!is_null($IDvariation)) {
|
|
$product->title = Variations::fillInProductTitle(intval($IDvariation), $product->title);
|
|
}
|
|
|
|
if ($IDvariation) {
|
|
$product->ean = returnSQLResult('SELECT ean FROM products_variations WHERE id=:id', ['id' => $IDvariation]);
|
|
if (findModule(Modules::PRODUCTS_VARIATIONS, Modules::SUB_CODE)) {
|
|
$product->code = returnSQLResult('SELECT code FROM products_variations WHERE id=:id', ['id' => $IDvariation]);
|
|
}
|
|
}
|
|
|
|
if (empty($row['id_product'])) {
|
|
if (!empty($product->note['title'])) {
|
|
$product['title'] = $product->note['title'];
|
|
}
|
|
|
|
if (!empty($product->note['vat'])) {
|
|
$product['vat'] = getVal('vat', $product->note, 0);
|
|
}
|
|
|
|
if (!empty($product->note['piece_price'])) {
|
|
$product->priceRaw = toDecimal($product->note['piece_price']);
|
|
}
|
|
}
|
|
$price = $product->getPrice($IDvariation, $product->note, $dec_Pieces);
|
|
|
|
$product->discount = getVal('discount', $product->note, $row['discount']);
|
|
|
|
// nove promenne s cenou
|
|
$priceArray = formatCustomerPrice($price, (float) $product->discount, getVat($row['vat']), $product->id);
|
|
|
|
$price_with_vat_rounded = roundPrice($priceArray['value_with_vat']);
|
|
$price_without_vat_rounded = calcPrice($price_with_vat_rounded, -getVat($row['vat']));
|
|
$item_price_with_vat = $price_with_vat_rounded->mul($dec_Pieces);
|
|
$item_price_without_vat = calcPrice($item_price_with_vat, -getVat($row['vat']));
|
|
$totalPriceArray = formatPrice($item_price_without_vat, getVat($row['vat']));
|
|
|
|
/* TODO rozbije to automotovelo - zaokrouhlovani na cely cisla, 122 * 3pcs -10% sleva */
|
|
$priceArray['value_without_vat'] = $price_without_vat_rounded;
|
|
|
|
$product->price = $priceArray;
|
|
$product->totalPrice = $totalPriceArray;
|
|
// celkova cena bez DPH
|
|
$this->totalPriceNoVat = $this->totalPriceNoVat->add($price_without_vat_rounded->mul($dec_Pieces));
|
|
|
|
// celkova cena s DPH
|
|
$this->totalPriceWithVat = $this->totalPriceWithVat->add($price_with_vat_rounded->mul($dec_Pieces));
|
|
|
|
$vat = $row['vat'];
|
|
|
|
// ceny podle DPH, vlozit cenu s DPH
|
|
if (!isset($this->priceByVat[$vat])) {
|
|
$this->priceByVat[$vat] = DecimalConstants::zero();
|
|
}
|
|
|
|
$this->priceByVat[$vat] = $this->priceByVat[$vat]->add($price_without_vat_rounded->mul($dec_Pieces));
|
|
|
|
// zapocitat celkovy pocet kusu
|
|
$this->totalPieces += $row['pieces'];
|
|
|
|
$this->products[$product->cart_item_id] = $product;
|
|
}
|
|
sqlFreeResult($SQL);
|
|
}
|
|
|
|
public static function findDeliveryTypeByPayMethod($id_method)
|
|
{
|
|
switch ($id_method) {
|
|
case Payment::METHOD_CASH:
|
|
$payment_class = 'Hotovost';
|
|
break;
|
|
case Payment::METHOD_CARD:
|
|
$payment_class = 'PlatebniKarta';
|
|
break;
|
|
case Payment::METHOD_INVOICE:
|
|
$payment_class = 'Prevodem';
|
|
break;
|
|
default:
|
|
throw new Exception('POS: undefined pay method !!');
|
|
}
|
|
|
|
$delivery_type = sqlQueryBuilder()
|
|
->select("dt.id as id, CONCAT_WS(' - ', dtp.name, dtd.name) as name")
|
|
->from('delivery_type', 'dt')
|
|
->join('dt', 'delivery_type_delivery', 'dtd', 'dt.id_delivery = dtd.id AND dtd.class = :delivery_class')
|
|
->join('dt', 'delivery_type_payment', 'dtp', 'dt.id_payment = dtp.id AND dtp.class = :payment_class')
|
|
->setParameters([
|
|
'payment_class' => $payment_class,
|
|
'delivery_class' => 'OsobniOdber',
|
|
])
|
|
->execute()
|
|
->fetchAll();
|
|
|
|
if (!$delivery_type) {
|
|
throw new Exception('POS: id_delivery_type not found!!');
|
|
}
|
|
|
|
return $delivery_type[0];
|
|
}
|
|
|
|
// Order Submission
|
|
public function submitOrder($languageID = null)
|
|
{
|
|
global $cfg, $ctrl, $dbcfg, $adminID;
|
|
|
|
$error = $this->checkCart();
|
|
|
|
if (empty($error)) {
|
|
$order = new Order();
|
|
|
|
sqlStartTransaction();
|
|
|
|
$fields = [
|
|
'id_user' => null,
|
|
'order_no' => '_'.rand(0, 999999),
|
|
'status' => 0,
|
|
'note_user' => $this->note,
|
|
'invoice_name' => 'Zákazník',
|
|
'delivery_type' => 'Kamenný obchod',
|
|
'date_created' => date('Y-m-d H:i'),
|
|
'date_updated' => date('Y-m-d H:i'),
|
|
'source' => OrderInfo::ORDER_SOURCE_POS,
|
|
'id_delivery' => $this->transport,
|
|
];
|
|
|
|
$SQL = $this->insertSQL('orders', $fields);
|
|
// TODO: non null - ["invoice_firm", "invoice_ico", "invoice_dic", "delivery_firm", "note_user", "invoice_country", "delivery_country"]
|
|
|
|
if ($SQL) {
|
|
// ID nove ulozene objednavky
|
|
$IDorder = sqlInsertId();
|
|
|
|
// Generate order ID
|
|
$orderNo = Orders::createOrderNumber($IDorder);
|
|
|
|
if (empty($orderNo)) {
|
|
logError(__FILE__, __LINE__, 'Empty orderNo!');
|
|
}
|
|
|
|
// Store generated order ID
|
|
$this->updateSQL('orders', [
|
|
'order_no' => $orderNo,
|
|
'admin' => $adminID ? $adminID : null,
|
|
], ['id' => $IDorder]);
|
|
|
|
// Create Order object
|
|
$order->createFromDB($IDorder, true, true, true);
|
|
|
|
// Set status indicating order creation
|
|
$order->status = -1;
|
|
|
|
// ulozit vybrane zbozi do objednavky
|
|
$products = [];
|
|
|
|
$cfg['Order']['hideCode'] = true;
|
|
foreach ($this->products as $product) {
|
|
$products[] = $product;
|
|
|
|
$discount = getVal('discount', $product->note, 0);
|
|
|
|
$fields = [
|
|
'discount' => $discount,
|
|
];
|
|
|
|
if (empty($product->id)) {
|
|
$res = $order->insertNonItem(
|
|
$order->id,
|
|
$product->price['value_without_vat']->mul(toDecimal($product->pieces)),
|
|
$product->price['value_without_vat'],
|
|
$product->pieces,
|
|
$product->price['vat'],
|
|
$product['title']
|
|
);
|
|
} else {
|
|
$res = $order->insertItem($product->id, $product->id_variation, $product->pieces, '', false);
|
|
$fields['piece_price'] = $product->price['value_without_vat'];
|
|
$fields['total_price'] = $product->price['value_without_vat']->mul(toDecimal($product->pieces));
|
|
$dispatcher = ServiceContainer::getService('event_dispatcher');
|
|
$event = $dispatcher->dispatch(
|
|
new OrderItemEvent($product, $product->id_variation, null, $product->pieces, null, $order),
|
|
OrderItemEvent::ITEM_UPDATED
|
|
);
|
|
}
|
|
|
|
$this->updateSQL('order_items',
|
|
$fields, ['id' => $res]);
|
|
|
|
if (false === $res) {
|
|
logError(__FILE__, __LINE__, "Order->insertItem failed: insertItem({$product->id}, {$product->id_variation}, {$product->pieces}, {$product->note}) cart item: {$product->cart_item_id}");
|
|
|
|
$_REQUEST['IDp'] = $product->id;
|
|
|
|
return 31;
|
|
}
|
|
|
|
// vymazat zbozi z kosiku
|
|
$this->deleteSQL('cart', ['id' => $product->cart_item_id]);
|
|
}
|
|
|
|
$this->clear();
|
|
|
|
$order->recalculate(true);
|
|
|
|
sqlFinishTransaction();
|
|
|
|
$order->logHistory('Vytvořeno v pokladně '.date(Settings::getDateFormat().' '.Settings::getTimeFormat(), time()));
|
|
|
|
return $order;
|
|
} else {
|
|
$error = 2;
|
|
}
|
|
}
|
|
|
|
return $error;
|
|
}
|
|
|
|
public function addNonItem($params)
|
|
{
|
|
return $this->insertSQL('cart', $params);
|
|
}
|
|
|
|
public function updateNonItem($params)
|
|
{
|
|
return $this->updateSQL('cart', ['pieces' => $params['pieces'], 'note' => $params['note']], ['id' => $params['id']]);
|
|
}
|
|
|
|
public static function getVats()
|
|
{
|
|
if (!empty($vats)) {
|
|
return $vats;
|
|
}
|
|
|
|
$SQL = sqlQuery('SELECT id, descr, vat, is_default
|
|
FROM '.getTableName('vats').'
|
|
ORDER BY vat DESC ');
|
|
static $vats = [];
|
|
|
|
foreach ($SQL as $key => $row) {
|
|
$vats[$row['id']] = $row;
|
|
}
|
|
|
|
return $vats;
|
|
}
|
|
|
|
public static function getVatByID($id_vat)
|
|
{
|
|
$vats = self::getVats();
|
|
|
|
if (!empty($vats[$id_vat]['vat'])) {
|
|
return $vats[$id_vat]['vat'];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public function getSelectParams($alias = '')
|
|
{
|
|
$userKey = self::getCartID();
|
|
|
|
return ['user_key' => $userKey];
|
|
}
|
|
|
|
protected function checkCart()
|
|
{
|
|
// Check cart is not empty
|
|
$where = queryCreate($this->getSelectParams(), true);
|
|
$products = returnSQLResult('SELECT COUNT(c.id)
|
|
FROM '.getTableName('cart').' AS c
|
|
LEFT JOIN '.getTableName('products')." AS p ON c.id_product=p.id
|
|
WHERE {$where}");
|
|
|
|
if (0 == intval($products)) {
|
|
return 12;
|
|
}
|
|
|
|
// Check all items are in store if required
|
|
return $this->checkCartItems();
|
|
}
|
|
|
|
public function checkCartItems(&$errors = null)
|
|
{
|
|
global $dbcfg, $cfg;
|
|
|
|
$where = queryCreate($this->getSelectParams(), true);
|
|
|
|
$SQL = sqlQuery("SELECT p.id as id_product
|
|
FROM cart AS c
|
|
LEFT JOIN products AS p ON c.id_product=p.id
|
|
LEFT JOIN products_variations pv ON pv.id_product = p.id
|
|
WHERE {$where} AND c.id_variation IS NULL AND pv.id IS NOT NULL
|
|
GROUP BY c.id");
|
|
if (sqlNumRows($SQL) > 0) {
|
|
logError(__FILE__, __LINE__, 'WARN: Není vybrana varianta');
|
|
$this->errors[self::$ERR_NOT_SELECTED_VARIATION] = $errors = sqlFetchAll($SQL, ['id_product' => 'id_product']);
|
|
|
|
return 31;
|
|
}
|
|
|
|
// Check for products not on stock
|
|
if (\Settings::ORDER_AVAILABILITY_ALL !== $dbcfg->order_availability) {
|
|
$where = queryCreate($this->getSelectParams(), true);
|
|
|
|
if (findModule('products', 'showMax')) {
|
|
$where .= ' AND (COALESCE(pv.in_store, p.in_store) < c.pieces OR c.pieces > COALESCE(pv.in_store_show_max, p.in_store_show_max, '.$cfg['Modules']['products']['showMax'].'))';
|
|
} else {
|
|
$where .= ' AND COALESCE(pv.in_store, p.in_store) < c.pieces';
|
|
}
|
|
|
|
$failedProducts = sqlFetchAll(sqlQuery("SELECT p.id, pv.id as id_variation
|
|
FROM cart AS c
|
|
LEFT JOIN products AS p ON c.id_product=p.id
|
|
LEFT JOIN products_variations AS pv ON c.id_variation=pv.id
|
|
WHERE {$where} AND p.id IS NOT NULL"),
|
|
['id' => 'id_variation']);
|
|
|
|
if (count($failedProducts) > 0) {
|
|
$this->errors[self::$ERR_OUT_OF_STOCK] = $errors = $failedProducts;
|
|
|
|
return 30;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function getPurchaseState(): PurchaseState
|
|
{
|
|
$cart = clone $this;
|
|
$cart->products = array_filter($cart->products, function ($product) {
|
|
return !empty($product['product']);
|
|
});
|
|
|
|
return $this->purchaseUtil->createStateFromCart($cart)->setSource(OrderInfo::ORDER_SOURCE_POS);
|
|
}
|
|
}
|
|
|
|
class POSInternalException extends Exception
|
|
{
|
|
}
|
|
|
|
if (empty($subclass)) {
|
|
class PosCart extends PosCartBase
|
|
{
|
|
}
|
|
}
|