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