From c057f4be76544466af62556237f7031c874f5f51 Mon Sep 17 00:00:00 2001 From: Nikita Kiselev Date: Thu, 31 Jul 2025 23:48:11 +0300 Subject: [PATCH] feat: create new order --- .../controller/extension/tgshop/handle.php | 214 +------------ .../upload/oc_telegram_shop/composer.json | 3 +- .../upload/oc_telegram_shop/composer.lock | 48 ++- .../framework/Http/Request.php | 18 +- .../Connections/ConnectionInterface.php | 6 + .../Connections/MySqlConnection.php | 45 ++- .../src/Decorators/OcRegistryDecorator.php | 36 +++ .../src/Handlers/CartHandler.php | 26 +- .../src/Handlers/OrderCreateHandler.php | 60 ---- .../src/Handlers/OrderHandler.php | 159 ++++++++++ .../src/Services/CartService.php | 300 ++++++++++++++++++ .../upload/oc_telegram_shop/src/routes.php | 5 +- spa/src/App.vue | 2 +- spa/src/components/Form/TgInput.vue | 46 +++ spa/src/components/Form/TgTextarea.vue | 30 ++ spa/src/main.js | 1 - spa/src/router.js | 2 + spa/src/stores/CartStore.js | 22 +- spa/src/stores/CheckoutStore.js | 44 +++ spa/src/style.css | 6 +- spa/src/utils/ftch.js | 41 ++- spa/src/views/Cart.vue | 12 +- spa/src/views/Checkout.vue | 68 +++- spa/src/views/OrderCreated.vue | 16 + 24 files changed, 891 insertions(+), 319 deletions(-) create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/src/Decorators/OcRegistryDecorator.php delete mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderHandler.php create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/CartService.php create mode 100644 spa/src/components/Form/TgInput.vue create mode 100644 spa/src/components/Form/TgTextarea.vue create mode 100644 spa/src/stores/CheckoutStore.js create mode 100644 spa/src/views/OrderCreated.vue diff --git a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php index 1905271..c078bde 100755 --- a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php +++ b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php @@ -2,6 +2,7 @@ use App\Adapters\OcModelCatalogProductAdapter; use App\ApplicationFactory; +use App\Decorators\OcRegistryDecorator; use Cart\Cart; use Cart\Currency; use Cart\Tax; @@ -25,8 +26,9 @@ class Controllerextensiontgshophandle extends Controller $app = ApplicationFactory::create([ 'oc_config_tax' => $this->config->get('config_tax'), 'oc_default_currency' => $this->config->get('config_currency'), + 'oc_customer_group_id' => $this->config->get('config_customer_group_id'), 'timezone' => $this->config->get('config_timezone', 'UTC'), - 'language_id' => (int)$this->config->get('config_language_id'), + 'language_id' => (int) $this->config->get('config_language_id'), 'shop_base_url' => HTTPS_SERVER, 'dir_image' => DIR_IMAGE, 'db' => [ @@ -61,222 +63,18 @@ class Controllerextensiontgshophandle extends Controller return new ImageTool(DIR_IMAGE, HTTPS_SERVER); }); - $app->bind(\Cart\Cart::class, function () { + $app->bind(Cart::class, function () { return $this->cart; }); - $app->bind(DB::class, function () { - return $this->db; + $app->bind(OcRegistryDecorator::class, function () { + return new OcRegistryDecorator($this->registry); }); $this->load->model('checkout/order'); - $app->bind('model_checkout_order', function () { - return $this->model_checkout_order; - }); - $this->session->data['language'] = $this->config->get('config_language'); $app->bootAndHandleRequest(); } - - public function cart() { - $this->load->language('checkout/cart'); - - if ($this->cart->hasProducts()) { - if (!$this->cart->hasStock() && (!$this->config->get('config_stock_checkout') || $this->config->get('config_stock_warning'))) { - $data['error_warning'] = $this->language->get('error_stock'); - } elseif (isset($this->session->data['error'])) { - $data['error_warning'] = $this->session->data['error']; - - unset($this->session->data['error']); - } else { - $data['error_warning'] = ''; - } - - if ($this->config->get('config_customer_price') && !$this->customer->isLogged()) { - $data['attention'] = sprintf($this->language->get('text_login'), $this->url->link('account/login'), $this->url->link('account/register')); - } else { - $data['attention'] = ''; - } - - if (isset($this->session->data['success'])) { - $data['success'] = $this->session->data['success']; - - unset($this->session->data['success']); - } else { - $data['success'] = ''; - } - - if ($this->config->get('config_cart_weight')) { - $data['weight'] = $this->weight->format($this->cart->getWeight(), $this->config->get('config_weight_class_id'), $this->language->get('decimal_point'), $this->language->get('thousand_point')); - } else { - $data['weight'] = ''; - } - - $this->load->model('tool/image'); - $this->load->model('tool/upload'); - - $data['products'] = array(); - - $products = $this->cart->getProducts(); - - foreach ($products as $product) { - $product_total = 0; - - foreach ($products as $product_2) { - if ($product_2['product_id'] == $product['product_id']) { - $product_total += $product_2['quantity']; - } - } - - if ($product['minimum'] > $product_total) { - $data['error_warning'] = sprintf($this->language->get('error_minimum'), $product['name'], $product['minimum']); - } - - if ($product['image']) { - $image = $this->model_tool_image->resize($product['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_cart_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_cart_height')); - } else { - $image = ''; - } - - $option_data = array(); - - foreach ($product['option'] as $option) { - if ($option['type'] != 'file') { - $value = $option['value']; - } else { - $upload_info = $this->model_tool_upload->getUploadByCode($option['value']); - - if ($upload_info) { - $value = $upload_info['name']; - } else { - $value = ''; - } - } - - $option_data[] = array( - 'name' => $option['name'], - 'value' => (utf8_strlen($value) > 20 ? utf8_substr($value, 0, 20) . '..' : $value) - ); - } - - // Display prices - if ($this->customer->isLogged() || !$this->config->get('config_customer_price')) { - $unit_price = $this->tax->calculate($product['price'], $product['tax_class_id'], $this->config->get('config_tax')); - - $price = $this->currency->format($unit_price, $this->session->data['currency']); - $total = $this->currency->format($unit_price * $product['quantity'], $this->session->data['currency']); - } else { - $price = false; - $total = false; - } - - $recurring = ''; - - if ($product['recurring']) { - $frequencies = array( - 'day' => $this->language->get('text_day'), - 'week' => $this->language->get('text_week'), - 'semi_month' => $this->language->get('text_semi_month'), - 'month' => $this->language->get('text_month'), - 'year' => $this->language->get('text_year') - ); - - if ($product['recurring']['trial']) { - $recurring = sprintf($this->language->get('text_trial_description'), $this->currency->format($this->tax->calculate($product['recurring']['trial_price'] * $product['quantity'], $product['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']), $product['recurring']['trial_cycle'], $frequencies[$product['recurring']['trial_frequency']], $product['recurring']['trial_duration']) . ' '; - } - - if ($product['recurring']['duration']) { - $recurring .= sprintf($this->language->get('text_payment_description'), $this->currency->format($this->tax->calculate($product['recurring']['price'] * $product['quantity'], $product['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']), $product['recurring']['cycle'], $frequencies[$product['recurring']['frequency']], $product['recurring']['duration']); - } else { - $recurring .= sprintf($this->language->get('text_payment_cancel'), $this->currency->format($this->tax->calculate($product['recurring']['price'] * $product['quantity'], $product['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']), $product['recurring']['cycle'], $frequencies[$product['recurring']['frequency']], $product['recurring']['duration']); - } - } - - $data['products'][] = array( - 'cart_id' => (int)$product['cart_id'], - 'thumb' => $image, - 'name' => $product['name'], - 'model' => $product['model'], - 'option' => $option_data, - 'recurring' => $recurring, - 'quantity' => (int)$product['quantity'], - 'stock' => $product['stock'] ? true : !(!$this->config->get('config_stock_checkout') || $this->config->get('config_stock_warning')), - 'reward' => ($product['reward'] ? sprintf($this->language->get('text_points'), $product['reward']) : ''), - 'price' => $price, - 'total' => $total, - 'href' => $this->url->link('product/product', 'product_id=' . $product['product_id']) - ); - } - - // Totals - $this->load->model('setting/extension'); - - $totals = array(); - $taxes = $this->cart->getTaxes(); - $total = 0; - - // Because __call can not keep var references so we put them into an array. - $total_data = array( - 'totals' => &$totals, - 'taxes' => &$taxes, - 'total' => &$total - ); - - $sort_order = array(); - - $results = $this->model_setting_extension->getExtensions('total'); - - foreach ($results as $key => $value) { - $sort_order[$key] = $this->config->get('total_' . $value['code'] . '_sort_order'); - } - - array_multisort($sort_order, SORT_ASC, $results); - - foreach ($results as $result) { - if ($this->config->get('total_' . $result['code'] . '_status')) { - $this->load->model('extension/total/' . $result['code']); - - // We have to put the totals in an array so that they pass by reference. - $this->{'model_extension_total_' . $result['code']}->getTotal($total_data); - } - } - - $sort_order = array(); - - foreach ($totals as $key => $value) { - $sort_order[$key] = $value['sort_order']; - } - - array_multisort($sort_order, SORT_ASC, $totals); - - $data['totals'] = array(); - - foreach ($totals as $total) { - $data['totals'][] = array( - 'title' => $total['title'], - 'text' => $this->currency->format($total['value'], $this->session->data['currency']) - ); - } - - $data['total_products_count'] = $this->cart->countProducts(); - } else { - $data['text_error'] = $this->language->get('text_empty'); - $data['totals'] = []; - $data['total'] = 0; - $data['products'] = []; - $data['total_products_count'] = 0; - unset($this->session->data['success']); - } - - http_response_code(200); - header('Content-Type: application/json'); - header('Access-Control-Allow-Origin: *'); - header('Access-Control-Allow-Methods: GET, POST'); - header('Access-Control-Allow-Headers: Content-Type, Authorization'); - header('Access-Control-Allow-Credentials: true'); - echo json_encode($data); - die(); - } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json index 74336aa..705da14 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json @@ -20,7 +20,8 @@ "ext-pdo": "*", "psr/container": "^2.0", "ext-json": "*", - "intervention/image": "^2.7" + "intervention/image": "^2.7", + "rakit/validation": "^1.4" }, "require-dev": { "roave/security-advisories": "dev-latest" diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock index 47a5793..ea8ba70 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4c731e75f55435f8b300c557fdfabfed", + "content-hash": "0862025c427a0e17dfde4fb54820c5c0", "packages": [ { "name": "guzzlehttp/psr7", @@ -367,6 +367,52 @@ }, "time": "2023-04-04T09:54:51+00:00" }, + { + "name": "rakit/validation", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/rakit/validation.git", + "reference": "ff003a35cdf5030a5f2482299f4c93f344a35b29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rakit/validation/zipball/ff003a35cdf5030a5f2482299f4c93f344a35b29", + "reference": "ff003a35cdf5030a5f2482299f4c93f344a35b29", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^6.5", + "squizlabs/php_codesniffer": "^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Rakit\\Validation\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Muhammad Syifa", + "email": "emsifa@gmail.com" + } + ], + "description": "PHP Laravel like standalone validation library", + "support": { + "issues": "https://github.com/rakit/validation/issues", + "source": "https://github.com/rakit/validation/tree/v1.4.0" + }, + "time": "2020-08-27T05:07:01+00:00" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3", diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php index 394ef31..e1aa6a7 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php @@ -15,13 +15,14 @@ class Request private $content; public function __construct( - array $query, - array $request, - array $cookies, - array $files, - array $server, + array $query, + array $request, + array $cookies, + array $files, + array $server, string $content = null - ) { + ) + { $this->query = $query; $this->request = $request; $this->cookies = $cookies; @@ -69,4 +70,9 @@ class Request return $this->query[$key] ?? $default; } + + public function getClientIp(): ?string + { + return $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? null; + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php index 3480ad1..ca7dfd4 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/ConnectionInterface.php @@ -31,4 +31,10 @@ interface ConnectionInterface public function lastInsertId($name = null): int; public function getVersion(): string; + + public function insert(string $table, array $data): bool; + + public function transaction(callable $callback): void; + + public function getLastError(): ?array; } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/MySqlConnection.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/MySqlConnection.php index a03fcbd..c3f804e 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/MySqlConnection.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/QueryBuilder/Connections/MySqlConnection.php @@ -9,6 +9,7 @@ use RuntimeException; class MySqlConnection implements ConnectionInterface { private $pdo; + private ?array $lastError = null; public function __construct(PDO $pdo) { @@ -37,7 +38,7 @@ class MySqlConnection implements ConnectionInterface return 'null'; case is_int($value) || is_float($value): - return (string)$value; + return (string) $value; case is_bool($value): return $value ? '1' : '0'; @@ -87,7 +88,21 @@ class MySqlConnection implements ConnectionInterface public function statement(string $sql, array $bindings = []): bool { - return $this->pdo->prepare($sql)->execute($bindings); + $statement = $this->pdo->prepare($sql); + + if (! $statement) { + $this->lastError = $this->pdo->errorInfo(); + return false; + } + + $success = $statement->execute($bindings); + + if (! $success) { + $this->lastError = $statement->errorInfo(); + return false; + } + + return $success; } public function beginTransaction(): bool @@ -128,4 +143,30 @@ class MySqlConnection implements ConnectionInterface ->query('SELECT VERSION()') ->fetch(PDO::FETCH_COLUMN); } + + public function insert(string $table, array $data): bool + { + $placeholders = implode(',', array_fill(0, count($data), '?')); + $columns = implode(',', array_map(static fn ($key) => "`${key}`", array_keys($data))); + $sql = sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, $columns, $placeholders); + + return $this->statement($sql, array_values($data)); + } + + public function transaction(callable $callback): void + { + try { + $this->beginTransaction(); + $callback(); + $this->commitTransaction(); + } catch (\Exception $exception) { + $this->rollBackTransaction(); + throw $exception; + } + } + + public function getLastError(): ?array + { + return $this->lastError; + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Decorators/OcRegistryDecorator.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Decorators/OcRegistryDecorator.php new file mode 100644 index 0000000..6c2e3a6 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Decorators/OcRegistryDecorator.php @@ -0,0 +1,36 @@ +registry = $registry; + } + + public function __get($key) + { + return $this->registry->get($key); + } + + public function __set($key, $value) + { + $this->registry->set($key, $value); + } + + public function __isset($key) + { + return $this->registry->has($key); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CartHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CartHandler.php index f758728..6ebcb89 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CartHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CartHandler.php @@ -2,6 +2,7 @@ namespace App\Handlers; +use App\Services\CartService; use Cart\Cart; use Openguru\OpenCartFramework\Http\JsonResponse; use Openguru\OpenCartFramework\Http\Request; @@ -11,12 +12,22 @@ class CartHandler { private Cart $cart; private ImageToolInterface $imageTool; + private CartService $cartService; - public function __construct(Cart $cart, \DB $database, ImageToolInterface $imageTool) + public function __construct(Cart $cart, ImageToolInterface $imageTool, CartService $cartService) { $this->cart = $cart; - $this->database = $database; $this->imageTool = $imageTool; + $this->cartService = $cartService; + } + + public function index(): JsonResponse + { + $items = $this->cartService->getCart(); + + return new JsonResponse([ + 'data' => $items, + ]); } public function checkout(Request $request): JsonResponse @@ -43,15 +54,4 @@ class CartHandler 'data' => $items, ]); } - - private function getProducts(): array - { - $products = $this->cart->getProducts(); - - foreach ($products as &$product) { - $product['thumb'] = $this->imageTool->resize($product['image'], 100, 100, 'placeholder.png'); - } - - return $products; - } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php deleted file mode 100755 index befd57c..0000000 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderCreateHandler.php +++ /dev/null @@ -1,60 +0,0 @@ -database = $database; - } - - public function handle(Request $request): JsonResponse - { - $now = date('Y-m-d H:i:s'); - $storeId = 0; - $storeName = 'Ваш магазин'; - - $sql = <<database->statement($sql); - - - return new JsonResponse([]); - } -} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderHandler.php new file mode 100755 index 0000000..2833c09 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/OrderHandler.php @@ -0,0 +1,159 @@ +database = $database; + $this->cartService = $cartService; + $this->oc = $registry; + $this->settings = $settings; + } + + public function store(Request $request): JsonResponse + { + $validator = new Validator(); + + $validation = $validator->make($request->json(), [ + 'firstName' => 'required', + 'lastName' => 'required', + 'email' => 'required|email', + 'phone' => 'required', + 'address' => 'required', + 'comment' => 'required', + ]); + + $validation->validate(); + + if ($validation->fails()) { + $errors = $validation->errors(); + return new JsonResponse([ + 'data' => $errors->firstOfAll(), + ], Response::HTTP_UNPROCESSABLE_ENTITY); + } + + $now = date('Y-m-d H:i:s'); + $storeId = 0; + $storeName = 'Ваш магазин'; + + $cart = $this->cartService->getCart(); + $total = $cart['total'] ?? 0; + $orderStatusId = 1; + $products = $cart['products'] ?? []; + $totals = $cart['totals'] ?? []; + $customerGroupId = $this->settings->get('oc_customer_group_id'); + + $languageId = $this->oc->config->get('config_language_id'); + $currencyId = $this->oc->currency->getId($this->oc->session->data['currency']); + $currencyCode = $this->oc->session->data['currency']; + $currencyValue = $this->oc->currency->getValue($this->oc->session->data['currency']); + + $orderData = [ + 'store_id' => $storeId, + 'store_name' => $storeName, + 'firstname' => $request->json('firstName'), + 'lastname' => $request->json('lastName'), + 'email' => $request->json('email'), + 'telephone' => $request->json('phone'), + 'comment' => $request->json('comment'), + 'shipping_address_1' => $request->json('address'), + 'total' => $total, + 'order_status_id' => $orderStatusId, + 'ip' => $request->getClientIp(), + 'date_added' => $now, + 'date_modified' => $now, + 'language_id' => $languageId, + 'currency_id' => $currencyId, + 'currency_code' => $currencyCode, + 'currency_value' => $currencyValue, + 'customer_group_id' => $customerGroupId, + ]; + + $this->database->transaction( + function () use ($orderData, $products, $totals, $orderStatusId, $now) { + $success = $this->database->insert(db_table('order'), $orderData); + + if (! $success) { + [, $error] = $this->database->getLastError(); + throw new RuntimeException("Failed to insert row into order. Error: $error"); + } + + $orderId = $this->database->lastInsertId(); + + // Insert products + foreach ($products as $product) { + $success = $this->database->insert(db_table('order_product'), [ + 'order_id' => $orderId, + 'product_id' => $product['product_id'], + 'name' => $product['name'], + 'model' => $product['model'], + 'quantity' => $product['quantity'], + 'price' => $product['price_numeric'], + 'total' => $product['total_numeric'], + 'reward' => $product['reward_numeric'], + ]); + + if (! $success) { + [, $error] = $this->database->getLastError(); + throw new RuntimeException("Failed to insert row into order_product. Error: $error"); + } + } + + // Insert totals + foreach ($totals as $total) { + $success = $this->database->insert(db_table('order_total'), [ + 'order_id' => $orderId, + 'code' => $total['code'], + 'title' => $total['title'], + 'value' => $total['value'], + 'sort_order' => $total['sort_order'], + ]); + + if (! $success) { + [, $error] = $this->database->getLastError(); + throw new RuntimeException("Failed to insert row into order_total. Error: $error"); + } + } + + // Insert history + $success = $this->database->insert(db_table('order_history'), [ + 'order_id' => $orderId, + 'order_status_id' => $orderStatusId, + 'notify' => 0, + 'comment' => 'Заказ оформлен через Telegram Mini App', + 'date_added' => $now, + ]); + + if (! $success) { + [, $error] = $this->database->getLastError(); + throw new RuntimeException("Failed to insert row into order_history. Error: $error"); + } + } + ); + + $this->cartService->flush(); + + return new JsonResponse([]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/CartService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/CartService.php new file mode 100644 index 0000000..8563c7f --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/CartService.php @@ -0,0 +1,300 @@ +oc = $registry; + $this->cart = $cart; + } + + public function getCart(): array + { + $this->oc->load->language('checkout/cart'); + + if ($this->oc->cart->hasProducts()) { + if (! $this->oc->cart->hasStock() && (! $this->oc->config->get('config_stock_checkout') || $this->oc->config->get( + 'config_stock_warning' + ))) { + $data['error_warning'] = $this->oc->language->get('error_stock'); + } elseif (isset($this->oc->session->data['error'])) { + $data['error_warning'] = $this->oc->session->data['error']; + + unset($this->oc->session->data['error']); + } else { + $data['error_warning'] = ''; + } + + if ($this->oc->config->get('config_customer_price') && ! $this->oc->customer->isLogged()) { + $data['attention'] = sprintf( + $this->oc->language->get('text_login'), + $this->oc->url->link('account/login'), + $this->oc->url->link('account/register') + ); + } else { + $data['attention'] = ''; + } + + if (isset($this->oc->session->data['success'])) { + $data['success'] = $this->oc->session->data['success']; + + unset($this->oc->session->data['success']); + } else { + $data['success'] = ''; + } + + if ($this->oc->config->get('config_cart_weight')) { + $data['weight'] = $this->oc->weight->format( + $this->oc->cart->getWeight(), + $this->oc->config->get('config_weight_class_id'), + $this->oc->language->get('decimal_point'), + $this->oc->language->get('thousand_point') + ); + } else { + $data['weight'] = ''; + } + + $this->oc->load->model('tool/image'); + $this->oc->load->model('tool/upload'); + + $data['products'] = array(); + + $products = $this->oc->cart->getProducts(); + + foreach ($products as $product) { + $product_total = 0; + + foreach ($products as $product_2) { + if ($product_2['product_id'] == $product['product_id']) { + $product_total += $product_2['quantity']; + } + } + + if ($product['minimum'] > $product_total) { + $data['error_warning'] = sprintf( + $this->oc->language->get('error_minimum'), + $product['name'], + $product['minimum'] + ); + } + + if ($product['image']) { + $image = $this->oc->model_tool_image->resize( + $product['image'], + $this->oc->config->get('theme_' . $this->oc->config->get('config_theme') . '_image_cart_width'), + $this->oc->config->get('theme_' . $this->oc->config->get('config_theme') . '_image_cart_height') + ); + } else { + $image = ''; + } + + $option_data = array(); + + foreach ($product['option'] as $option) { + if ($option['type'] != 'file') { + $value = $option['value']; + } else { + $upload_info = $this->oc->model_tool_upload->getUploadByCode($option['value']); + + if ($upload_info) { + $value = $upload_info['name']; + } else { + $value = ''; + } + } + + $option_data[] = array( + 'name' => $option['name'], + 'value' => (utf8_strlen($value) > 20 ? utf8_substr($value, 0, 20) . '..' : $value) + ); + } + + $priceNumeric = 0; + $totalNumeric = 0; + + // Display prices + if ($this->oc->customer->isLogged() || ! $this->oc->config->get('config_customer_price')) { + $unit_price = $this->oc->tax->calculate( + $product['price'], + $product['tax_class_id'], + $this->oc->config->get('config_tax') + ); + + $priceNumeric = $unit_price; + $totalNumeric = $unit_price * $product['quantity']; + + $price = $this->oc->currency->format($unit_price, $this->oc->session->data['currency']); + $total = $this->oc->currency->format($totalNumeric, $this->oc->session->data['currency']); + } else { + $price = false; + $total = false; + } + + $recurring = ''; + + if ($product['recurring']) { + $frequencies = array( + 'day' => $this->oc->language->get('text_day'), + 'week' => $this->oc->language->get('text_week'), + 'semi_month' => $this->oc->language->get('text_semi_month'), + 'month' => $this->oc->language->get('text_month'), + 'year' => $this->oc->language->get('text_year') + ); + + if ($product['recurring']['trial']) { + $recurring = sprintf( + $this->oc->language->get('text_trial_description'), + $this->oc->currency->format( + $this->oc->tax->calculate( + $product['recurring']['trial_price'] * $product['quantity'], + $product['tax_class_id'], + $this->oc->config->get('config_tax') + ), + $this->oc->session->data['currency'] + ), + $product['recurring']['trial_cycle'], + $frequencies[$product['recurring']['trial_frequency']], + $product['recurring']['trial_duration'] + ) . ' '; + } + + if ($product['recurring']['duration']) { + $recurring .= sprintf( + $this->oc->language->get('text_payment_description'), + $this->oc->currency->format( + $this->oc->tax->calculate( + $product['recurring']['price'] * $product['quantity'], + $product['tax_class_id'], + $this->oc->config->get('config_tax') + ), + $this->oc->session->data['currency'] + ), + $product['recurring']['cycle'], + $frequencies[$product['recurring']['frequency']], + $product['recurring']['duration'] + ); + } else { + $recurring .= sprintf( + $this->oc->language->get('text_payment_cancel'), + $this->oc->currency->format( + $this->oc->tax->calculate( + $product['recurring']['price'] * $product['quantity'], + $product['tax_class_id'], + $this->oc->config->get('config_tax') + ), + $this->oc->session->data['currency'] + ), + $product['recurring']['cycle'], + $frequencies[$product['recurring']['frequency']], + $product['recurring']['duration'] + ); + } + } + + $data['products'][] = array( + 'product_id' => (int) $product['product_id'], + 'cart_id' => (int) $product['cart_id'], + 'thumb' => $image, + 'name' => $product['name'], + 'model' => $product['model'], + 'option' => $option_data, + 'recurring' => $recurring, + 'quantity' => (int) $product['quantity'], + 'stock' => $product['stock'] ? true : ! (! $this->oc->config->get( + 'config_stock_checkout' + ) || $this->oc->config->get('config_stock_warning')), + 'reward' => ($product['reward'] ? sprintf( + $this->oc->language->get('text_points'), + $product['reward'] + ) : ''), + 'price' => $price, + 'total' => $total, + 'href' => $this->oc->url->link('product/product', 'product_id=' . $product['product_id']), + + 'price_numeric' => $priceNumeric, + 'total_numeric' => $totalNumeric, + 'reward_numeric' => $product['reward'] ?? 0, + ); + } + + // Totals + $this->oc->load->model('setting/extension'); + + $totals = array(); + $taxes = $this->oc->cart->getTaxes(); + $total = 0; + + // Because __call can not keep var references so we put them into an array. + $total_data = array( + 'totals' => &$totals, + 'taxes' => &$taxes, + 'total' => &$total + ); + + $sort_order = array(); + + $results = $this->oc->model_setting_extension->getExtensions('total'); + + foreach ($results as $key => $value) { + $sort_order[$key] = $this->oc->config->get('total_' . $value['code'] . '_sort_order'); + } + + array_multisort($sort_order, SORT_ASC, $results); + + foreach ($results as $result) { + if ($this->oc->config->get('total_' . $result['code'] . '_status')) { + $this->oc->load->model('extension/total/' . $result['code']); + + // We have to put the totals in an array so that they pass by reference. + $this->oc->{'model_extension_total_' . $result['code']}->getTotal($total_data); + } + } + + $sort_order = array(); + + foreach ($totals as $key => $value) { + $sort_order[$key] = $value['sort_order']; + } + + array_multisort($sort_order, SORT_ASC, $totals); + + $data['totals'] = array(); + + foreach ($totals as $total) { + $data['totals'][] = [ + 'code' => $total['code'], + 'title' => $total['title'], + 'value' => $total['value'], + 'sort_order' => $total['sort_order'], + 'text' => $this->oc->currency->format($total['value'], $this->oc->session->data['currency']), + ]; + } + + $lastTotal = $totals[count($totals) - 1] ?? false; + $data['total'] = $lastTotal ? $lastTotal['value'] : 0; + $data['total_products_count'] = $this->oc->cart->countProducts(); + } else { + $data['text_error'] = $this->oc->language->get('text_empty'); + $data['totals'] = []; + $data['total'] = 0; + $data['products'] = []; + $data['total_products_count'] = 0; + unset($this->oc->session->data['success']); + } + + return $data; + } + + public function flush(): void + { + $this->cart->clear(); + } +} \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php index 66cec69..093a8ed 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php @@ -3,15 +3,16 @@ use App\Handlers\CategoriesHandler; use App\Handlers\CartHandler; use App\Handlers\HelloWorldHandler; -use App\Handlers\OrderCreateHandler; +use App\Handlers\OrderHandler; use App\Handlers\ProductsHandler; return [ 'products' => [ProductsHandler::class, 'handle'], 'product_show' => [ProductsHandler::class, 'show'], - 'order_create' => [OrderCreateHandler::class, 'handle'], + 'storeOrder' => [OrderHandler::class, 'store'], 'categoriesList' => [CategoriesHandler::class, 'index'], 'checkout' => [CartHandler::class, 'checkout'], + 'getCart' => [CartHandler::class, 'index'], ]; diff --git a/spa/src/App.vue b/spa/src/App.vue index dbf09ab..9d13aee 100644 --- a/spa/src/App.vue +++ b/spa/src/App.vue @@ -1,5 +1,5 @@