feat(products): show correct product prices
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class EntityNotFoundException extends Exception implements NonLoggableExceptionInterface
|
||||
{
|
||||
public function __construct($message = "Entity Not Found", $code = 404, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,14 @@ namespace Openguru\OpenCartFramework\OpenCart\Decorators;
|
||||
use Cart\Cart;
|
||||
use Cart\Currency;
|
||||
use Config;
|
||||
use Language;
|
||||
use Loader;
|
||||
use ModelCatalogProduct;
|
||||
use ModelSettingSetting;
|
||||
use ModelToolImage;
|
||||
use Registry;
|
||||
use Session;
|
||||
use Url;
|
||||
|
||||
/**
|
||||
* @property Loader $load
|
||||
@@ -17,6 +20,9 @@ use Session;
|
||||
* @property Session $session
|
||||
* @property Currency $currency
|
||||
* @property Config $config
|
||||
* @property Url $url
|
||||
* @property Language $language
|
||||
* @property ModelToolImage $model_tool_image
|
||||
* @property ModelCatalogProduct $model_catalog_product
|
||||
* @property ModelSettingSetting $model_setting_setting
|
||||
*/
|
||||
|
||||
@@ -19,4 +19,13 @@ class OcModelCatalogProductAdapter
|
||||
{
|
||||
return $this->model->getProductOptions($productId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $productId
|
||||
* @return array|false
|
||||
*/
|
||||
public function getProduct(int $productId)
|
||||
{
|
||||
return $this->model->getProduct($productId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ namespace App\Handlers;
|
||||
use App\Services\ProductsService;
|
||||
use Exception;
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -25,7 +27,7 @@ class ProductsHandler
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function handle(Request $request): JsonResponse
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$page = (int) $request->get('page', 1);
|
||||
$perPage = min((int) $request->get('perPage', 6), 15);
|
||||
@@ -60,14 +62,11 @@ class ProductsHandler
|
||||
$imageFullHeight = 1000;
|
||||
|
||||
try {
|
||||
$product = $this->productsService->getProduct(
|
||||
$productId,
|
||||
$languageId,
|
||||
$imageWidth,
|
||||
$imageHeight,
|
||||
$imageFullWidth,
|
||||
$imageFullHeight
|
||||
);
|
||||
$product = $this->productsService->getProductById($productId);
|
||||
} catch (EntityNotFoundException $exception) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Product with id ' . $productId . ' not found',
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->logException($exception);
|
||||
throw new RuntimeException('Error get product with id ' . $productId, 500, $exception);
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Adapters\OcModelCatalogProductAdapter;
|
||||
use Cart\Currency;
|
||||
use Cart\Tax;
|
||||
use Exception;
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\RawExpression;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Support\PaginationHelper;
|
||||
|
||||
@@ -21,7 +22,6 @@ class ProductsService
|
||||
private Currency $currency;
|
||||
private Tax $tax;
|
||||
private Settings $settings;
|
||||
private OcModelCatalogProductAdapter $ocModelCatalogProduct;
|
||||
private ImageToolInterface $ocImageTool;
|
||||
private OcRegistryDecorator $oc;
|
||||
private LoggerInterface $logger;
|
||||
@@ -31,7 +31,6 @@ class ProductsService
|
||||
Currency $currency,
|
||||
Tax $tax,
|
||||
Settings $settings,
|
||||
OcModelCatalogProductAdapter $ocModelCatalogProduct,
|
||||
ImageToolInterface $ocImageTool,
|
||||
OcRegistryDecorator $registry,
|
||||
LoggerInterface $logger
|
||||
@@ -40,7 +39,6 @@ class ProductsService
|
||||
$this->currency = $currency;
|
||||
$this->tax = $tax;
|
||||
$this->settings = $settings;
|
||||
$this->ocModelCatalogProduct = $ocModelCatalogProduct;
|
||||
$this->ocImageTool = $ocImageTool;
|
||||
$this->oc = $registry;
|
||||
$this->logger = $logger;
|
||||
@@ -71,6 +69,16 @@ class ProductsService
|
||||
->value('name');
|
||||
}
|
||||
|
||||
$customerGroupId = (int)$this->oc->config->get('config_customer_group_id');
|
||||
$specialPriceSql = "(SELECT price
|
||||
FROM oc_product_special ps
|
||||
WHERE ps.product_id = products.product_id
|
||||
AND ps.customer_group_id = $customerGroupId
|
||||
AND ((ps.date_start = '0000-00-00' OR ps.date_start < NOW()) AND
|
||||
(ps.date_end = '0000-00-00' OR ps.date_end > NOW()))
|
||||
ORDER BY ps.priority ASC, ps.price ASC
|
||||
LIMIT 1) AS special";
|
||||
|
||||
$productsQuery = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
@@ -79,6 +87,7 @@ class ProductsService
|
||||
'products.price' => 'price',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
new RawExpression($specialPriceSql),
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
@@ -166,6 +175,18 @@ class ProductsService
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
|
||||
$special = false;
|
||||
if ($product['special'] && (float) $product['special'] >= 0) {
|
||||
$special = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['special'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($productsImagesMap[$product['product_id']])) {
|
||||
$allImages = array_merge($allImages, $productsImagesMap[$product['product_id']]);
|
||||
}
|
||||
@@ -175,6 +196,7 @@ class ProductsService
|
||||
'product_quantity' => (int) $product['product_quantity'],
|
||||
'name' => html_entity_decode($product['product_name'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'price' => $price,
|
||||
'special' => $special,
|
||||
'images' => $allImages,
|
||||
];
|
||||
}, $products),
|
||||
@@ -187,74 +209,71 @@ class ProductsService
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EntityNotFoundException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getProduct(
|
||||
int $productId,
|
||||
int $languageId,
|
||||
int $imageWidth,
|
||||
int $imageHeight,
|
||||
int $imageFullWidth,
|
||||
int $imageFullHeight
|
||||
): array {
|
||||
$product = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'product_description.name' => 'product_name',
|
||||
'product_description.description' => 'product_description',
|
||||
'products.price' => 'price',
|
||||
'products.minimum' => 'minimum',
|
||||
'products.quantity' => 'quantity',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
'manufacturer.name' => 'product_manufacturer',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
db_table('manufacturer') . ' AS manufacturer',
|
||||
function (JoinClause $join) {
|
||||
$join->on('products.manufacturer_id', '=', 'manufacturer.manufacturer_id');
|
||||
}
|
||||
)
|
||||
->where('products.product_id', '=', $productId)
|
||||
->limit(1)
|
||||
->firstOrNull();
|
||||
public function getProductById(int $productId): array
|
||||
{
|
||||
$this->oc->load->language('product/product');
|
||||
$this->oc->load->model('catalog/category');
|
||||
$this->oc->load->model('catalog/manufacturer');
|
||||
$this->oc->load->model('catalog/product');
|
||||
$this->oc->load->model('catalog/review');
|
||||
$this->oc->load->model('tool/image');
|
||||
|
||||
if (! $product) {
|
||||
return [];
|
||||
$imageThumbWidth = 500;
|
||||
$imageThumbHeight = 500;
|
||||
$imageFullWidth = 1000;
|
||||
$imageFullHeight = 1000;
|
||||
$configTax = $this->oc->config->get('config_tax');
|
||||
|
||||
$product_info = $this->oc->model_catalog_product->getProduct($productId);
|
||||
|
||||
if (! $product_info) {
|
||||
throw new EntityNotFoundException('Product with id ' . $productId . ' not found');
|
||||
}
|
||||
|
||||
$productsImages = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products_images.product_id' => 'product_id',
|
||||
'products_images.image' => 'image',
|
||||
])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->orderBy('products_images.sort_order')
|
||||
->where('products_images.product_id', '=', $productId)
|
||||
->get();
|
||||
$data = [];
|
||||
$data['text_minimum'] = sprintf($this->oc->language->get('text_minimum'), $product_info['minimum']);
|
||||
|
||||
$imagePaths = [];
|
||||
if ($product['product_image']) {
|
||||
$imagePaths[] = $product['product_image'];
|
||||
$data['tab_review'] = sprintf($this->oc->language->get('tab_review'), $product_info['reviews']);
|
||||
|
||||
$data['product_id'] = $productId;
|
||||
$data['name'] = $product_info['name'];
|
||||
$data['manufacturer'] = $product_info['manufacturer'];
|
||||
$data['model'] = $product_info['model'];
|
||||
$data['reward'] = $product_info['reward'];
|
||||
$data['points'] = (int) $product_info['points'];
|
||||
$data['description'] = html_entity_decode($product_info['description'], ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if ($product_info['quantity'] <= 0) {
|
||||
$data['stock'] = $product_info['stock_status'];
|
||||
} elseif ($this->oc->config->get('config_stock_display')) {
|
||||
$data['stock'] = $product_info['quantity'];
|
||||
} else {
|
||||
$data['stock'] = $this->oc->language->get('text_instock');
|
||||
}
|
||||
foreach ($productsImages as $item) {
|
||||
$imagePaths[] = $item['image'];
|
||||
|
||||
$allImages = [];
|
||||
if ($product_info['image']) {
|
||||
$allImages[] = $product_info['image'];
|
||||
}
|
||||
$results = $this->oc->model_catalog_product->getProductImages($productId);
|
||||
foreach ($results as $result) {
|
||||
$allImages[] = $result['image'];
|
||||
}
|
||||
|
||||
$images = [];
|
||||
foreach ($imagePaths as $imagePath) {
|
||||
foreach ($allImages as $imagePath) {
|
||||
try {
|
||||
[$width, $height] = $this->ocImageTool->getRealSize($imagePath);
|
||||
$images[] = [
|
||||
'thumbnailURL' => $this->ocImageTool->resize($imagePath, $imageWidth, $imageHeight, 'placeholder.png'),
|
||||
'thumbnailURL' => $this->ocImageTool->resize(
|
||||
$imagePath,
|
||||
$imageThumbWidth,
|
||||
$imageThumbHeight,
|
||||
'placeholder.png'
|
||||
),
|
||||
'largeURL' => $this->ocImageTool->resize(
|
||||
$imagePath,
|
||||
$imageFullWidth,
|
||||
@@ -263,96 +282,136 @@ class ProductsService
|
||||
),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'alt' => html_entity_decode($product['product_name'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'alt' => html_entity_decode($product_info['name'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
$price = $this->currency->format(
|
||||
$data['images'] = $images;
|
||||
|
||||
$data['price'] = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
$product_info['price'],
|
||||
$product_info['tax_class_id'],
|
||||
$configTax,
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
$this->oc->session->data['currency']
|
||||
);
|
||||
|
||||
return [
|
||||
'id' => $product['product_id'],
|
||||
'name' => html_entity_decode($product['product_name'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'description' => html_entity_decode($product['product_description'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'manufacturer' => html_entity_decode($product['product_manufacturer'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'price' => $price,
|
||||
'minimum' => $product['minimum'],
|
||||
'quantity' => $product['quantity'],
|
||||
'images' => $images,
|
||||
'options' => $this->loadProductOptions($product),
|
||||
'attributes' => $this->loadProductAttributes($product['product_id']),
|
||||
];
|
||||
}
|
||||
if (! is_null($product_info['special']) && (float) $product_info['special'] >= 0) {
|
||||
$data['special'] = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product_info['special'],
|
||||
$product_info['tax_class_id'],
|
||||
$configTax,
|
||||
),
|
||||
$this->oc->session->data['currency']
|
||||
);
|
||||
$tax_price = (float) $product_info['special'];
|
||||
} else {
|
||||
$data['special'] = false;
|
||||
$tax_price = (float) $product_info['price'];
|
||||
}
|
||||
|
||||
private function loadProductOptions($product): array
|
||||
{
|
||||
$result = [];
|
||||
$productId = $product['product_id'];
|
||||
$taxClassId = $product['tax_class_id'];
|
||||
if ($configTax) {
|
||||
$data['tax'] = $this->currency->format($tax_price, $this->oc->session->data['currency']);
|
||||
} else {
|
||||
$data['tax'] = false;
|
||||
}
|
||||
|
||||
$options = $this->ocModelCatalogProduct->getProductOptions($productId);
|
||||
$ocConfigTax = $this->settings->get('oc_config_tax');
|
||||
$ocDefaultCurrency = $this->settings->get('oc_default_currency');
|
||||
$discounts = $this->oc->model_catalog_product->getProductDiscounts($productId);
|
||||
|
||||
foreach ($options as $option) {
|
||||
$data['discounts'] = [];
|
||||
|
||||
foreach ($discounts as $discount) {
|
||||
$data['discounts'][] = array(
|
||||
'quantity' => $discount['quantity'],
|
||||
'price' => $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$discount['price'],
|
||||
$product_info['tax_class_id'],
|
||||
$configTax,
|
||||
),
|
||||
$this->oc->session->data['currency']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$data['options'] = [];
|
||||
|
||||
foreach ($this->oc->model_catalog_product->getProductOptions($productId) as $option) {
|
||||
$product_option_value_data = [];
|
||||
|
||||
foreach ($option['product_option_value'] as $option_value) {
|
||||
if (! $option_value['subtract'] || ($option_value['quantity'] > 0)) {
|
||||
if ((float) $option_value['price']) {
|
||||
$priceWithTax = $this->tax->calculate(
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$option_value['price'],
|
||||
$taxClassId,
|
||||
$ocConfigTax ? 'Р' : false,
|
||||
);
|
||||
$product_info['tax_class_id'],
|
||||
$configTax ? 'P' : false
|
||||
),
|
||||
$this->oc->session->data['currency']
|
||||
);
|
||||
|
||||
$price = $this->currency->format($priceWithTax, $ocDefaultCurrency);
|
||||
} else {
|
||||
$price = false;
|
||||
}
|
||||
|
||||
$product_option_value_data[] = [
|
||||
'product_option_value_id' => (int) $option_value['product_option_value_id'],
|
||||
'option_value_id' => (int) $option_value['option_value_id'],
|
||||
$product_option_value_data[] = array(
|
||||
'product_option_value_id' => $option_value['product_option_value_id'],
|
||||
'option_value_id' => $option_value['option_value_id'],
|
||||
'name' => $option_value['name'],
|
||||
'image' => $this->ocImageTool->resize($option_value['image'], 50, 50),
|
||||
'image' => $this->oc->model_tool_image->resize($option_value['image'], 50, 50),
|
||||
'price' => $price,
|
||||
'price_prefix' => $option_value['price_prefix'],
|
||||
'selected' => false,
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'product_option_id' => (int) $option['product_option_id'],
|
||||
'values' => $product_option_value_data,
|
||||
'option_id' => (int) $option['option_id'],
|
||||
$data['options'][] = array(
|
||||
'product_option_id' => $option['product_option_id'],
|
||||
'product_option_value' => $product_option_value_data,
|
||||
'option_id' => $option['option_id'],
|
||||
'name' => $option['name'],
|
||||
'type' => $option['type'],
|
||||
'value' => $option['value'],
|
||||
'required' => filter_var($option['required'], FILTER_VALIDATE_BOOLEAN),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
if ($product_info['minimum']) {
|
||||
$data['minimum'] = (int) $product_info['minimum'];
|
||||
} else {
|
||||
$data['minimum'] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadProductAttributes(int $productId): array
|
||||
{
|
||||
$this->oc->load->model('catalog/product');
|
||||
$data['review_status'] = $this->oc->config->get('config_review_status');
|
||||
|
||||
return $this->oc->model_catalog_product->getProductAttributes($productId);
|
||||
$data['review_guest'] = true;
|
||||
|
||||
$data['customer_name'] = 'John Doe';
|
||||
|
||||
$data['reviews'] = sprintf($this->oc->language->get('text_reviews'), (int) $product_info['reviews']);
|
||||
$data['rating'] = (int) $product_info['rating'];
|
||||
|
||||
$data['attribute_groups'] = $this->oc->model_catalog_product->getProductAttributes($productId);
|
||||
|
||||
$data['tags'] = array();
|
||||
|
||||
if ($product_info['tag']) {
|
||||
$tags = explode(',', $product_info['tag']);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$data['tags'][] = array(
|
||||
'tag' => trim($tag),
|
||||
'href' => $this->oc->url->link('product/search', 'tag=' . trim($tag))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$data['recurrings'] = $this->oc->model_catalog_product->getProfiles($productId);
|
||||
|
||||
$this->oc->model_catalog_product->updateViewed($productId);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use App\Handlers\SettingsHandler;
|
||||
use App\Handlers\TelegramHandler;
|
||||
|
||||
return [
|
||||
'products' => [ProductsHandler::class, 'handle'],
|
||||
'products' => [ProductsHandler::class, 'index'],
|
||||
'product_show' => [ProductsHandler::class, 'show'],
|
||||
'storeOrder' => [OrderHandler::class, 'store'],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user