Files
kupshop/class/class.PosCart.php
2025-08-02 16:30:27 +02:00

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
{
}
}