ver. 0.294: Remove all 12 legacy autoload/shop/ classes (~2363 lines)

Complete Domain-Driven Architecture migration:
- Phase 1-4: Transport, ProductSet, Coupon, Shop, Search, Basket,
  ProductCustomField, Category, ProductAttribute, Promotion
- Phase 5: Order (~562 lines) + Product (~952 lines)
- ~20 Product methods migrated to ProductRepository
- Apilo sync migrated to OrderAdminService
- Production hotfixes: stale Redis cache (prices 0.00), unqualified
  Product:: refs in LayoutEngine, object->array template conversion
- AttributeRepository::getAttributeValueById() Redis cache added

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 02:05:39 +01:00
parent 22530df501
commit 69e78ca248
74 changed files with 2176 additions and 2669 deletions

View File

@@ -484,7 +484,16 @@ class AttributeRepository
}
$languageId = $langId !== null && trim($langId) !== '' ? trim($langId) : $this->defaultLanguageId();
return (string)$this->db->get(
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "AttributeRepository::getAttributeValueById:{$valueId}:{$languageId}";
$cached = $cacheHandler->get($cacheKey);
if ($cached !== false && $cached !== null) {
return (string)unserialize($cached);
}
$name = (string)$this->db->get(
'pp_shop_attributes_values_langs',
'name',
[
@@ -494,6 +503,10 @@ class AttributeRepository
],
]
);
$cacheHandler->set($cacheKey, $name);
return $name;
}
/**
@@ -939,4 +952,66 @@ class AttributeRepository
{
return round($value, $precision);
}
public function isValueDefault(int $valueId)
{
if ($valueId <= 0) {
return null;
}
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "attr_value_default:{$valueId}";
$cached = $cacheHandler->get($cacheKey);
if ($cached) {
return unserialize($cached);
}
$isDefault = $this->db->get('pp_shop_attributes_values', 'is_default', ['id' => $valueId]);
$cacheHandler->set($cacheKey, $isDefault);
return $isDefault;
}
public function getAttributeOrder(int $attributeId)
{
if ($attributeId <= 0) {
return null;
}
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "attr_order:{$attributeId}";
$cached = $cacheHandler->get($cacheKey);
if ($cached) {
return unserialize($cached);
}
$order = $this->db->get('pp_shop_attributes', 'o', ['id' => $attributeId]);
$cacheHandler->set($cacheKey, $order);
return $order;
}
public function getAttributeNameByValue(int $valueId, string $langId)
{
if ($valueId <= 0) {
return null;
}
$stmt = $this->db->query(
'SELECT name FROM pp_shop_attributes_langs AS psal '
. 'INNER JOIN pp_shop_attributes_values AS psav ON psal.attribute_id = psav.attribute_id '
. 'WHERE psav.id = :value_id AND lang_id = :lang_id',
[':value_id' => $valueId, ':lang_id' => $langId]
);
if (!$stmt) {
return null;
}
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
return is_array($row) ? ($row['name'] ?? null) : null;
}
}

View File

@@ -31,14 +31,15 @@ class BasketCalculator
global $lang_id;
$summary = 0;
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
if (is_array($basket)) {
foreach ($basket as $position) {
$product = \shop\Product::getFromCache((int)$position['product-id'], $lang_id);
$product = $productRepo->findCached((int)$position['product-id'], $lang_id);
$product_price_tmp = \shop\Product::calculate_basket_product_price(
(float)$product['price_brutto_promo'],
(float)$product['price_brutto'],
$product_price_tmp = self::calculateBasketProductPrice(
(float)($product['price_brutto_promo'] ?? 0),
(float)($product['price_brutto'] ?? 0),
$coupon,
$position
);
@@ -59,4 +60,161 @@ class BasketCalculator
}
return $count;
}
public static function validateBasket($basket)
{
if ( !is_array( $basket ) )
return array();
return $basket;
}
public static function checkProductQuantityInStock($basket, bool $message = false)
{
$result = false;
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
foreach ( $basket as $key => $val )
{
$permutation = null;
if ( isset( $val['parent_id'] ) and (int)$val['parent_id'] and isset( $val['product-id'] ) )
$permutation = $productRepo->getProductPermutationHash( (int)$val['product-id'] );
if ( !$permutation and isset( $val['attributes'] ) and is_array( $val['attributes'] ) and count( $val['attributes'] ) )
$permutation = implode( '|', $val['attributes'] );
$quantity_options = $productRepo->getProductPermutationQuantityOptions(
$val['parent_id'] ? $val['parent_id'] : $val['product-id'],
$permutation
);
if (
(int)$basket[ $key ][ 'quantity' ] < 1
and ( (int)$quantity_options['quantity'] > 0 or (int)$quantity_options['stock_0_buy'] === 1 )
)
{
$basket[ $key ][ 'quantity' ] = 1;
$result = true;
}
if ( ( $val[ 'quantity' ] > $quantity_options['quantity'] ) and !$quantity_options['stock_0_buy'] )
{
$basket[ $key ][ 'quantity' ] = $quantity_options['quantity'];
if ( $message )
\Shared\Helpers\Helpers::error( 'Ilość jednego lub więcej produktów została zmniejszona z powodu niestarczających stanów magazynowych. Sprawdź proszę koszyk.' );
$result = true;
}
}
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
return $result;
}
/**
* Calculate product price in basket (with coupon + promotion discounts).
* Migrated from \shop\Product::calculate_basket_product_price()
*/
public static function calculateBasketProductPrice( float $price_brutto_promo, float $price_brutto, $coupon, $basket_position )
{
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
// Produkty przecenione
if ( $price_brutto_promo )
{
$price['price'] = $price_brutto;
$price['price_new'] = $price_brutto_promo;
$coupon_type = is_array($coupon) ? ($coupon['type'] ?? null) : (is_object($coupon) ? $coupon->type : null);
$coupon_include_discounted = is_array($coupon) ? ($coupon['include_discounted_product'] ?? null) : (is_object($coupon) ? $coupon->include_discounted_product : null);
$coupon_categories = is_array($coupon) ? ($coupon['categories'] ?? null) : (is_object($coupon) ? $coupon->categories : null);
$coupon_amount = is_array($coupon) ? ($coupon['amount'] ?? 0) : (is_object($coupon) ? $coupon->amount : 0);
if ( $coupon_type && $coupon_include_discounted )
{
if ( $coupon_categories != null )
{
$cats = is_string($coupon_categories) ? json_decode($coupon_categories) : $coupon_categories;
$product_categories = $productRepo->productCategories( (int)$basket_position['parent_id'] ? (int)$basket_position['parent_id'] : (int)$basket_position['product-id'] );
if ( is_array( $cats ) ) foreach ( $cats as $category_tmp )
{
if ( in_array( $category_tmp, $product_categories ) )
{
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
break;
}
}
}
else
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
{
if ( $basket_position['discount_type'] == 3 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
if ( $basket_position['discount_type'] == 1 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price_new'] - $price['price_new'] * $basket_position['discount_amount'] / 100 );
}
}
else
{
if ( $basket_position['discount_amount'] && $basket_position['include_product_promo'] )
{
if ( $basket_position['discount_type'] == 3 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
if ( $basket_position['discount_type'] == 1 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price_new'] - $price['price_new'] * $basket_position['discount_amount'] / 100 );
}
}
}
// Produkt nieprzeceniony
else
{
$price['price'] = $price_brutto;
$price['price_new'] = $price_brutto;
$coupon_type = is_array($coupon) ? ($coupon['type'] ?? null) : (is_object($coupon) ? $coupon->type : null);
$coupon_categories = is_array($coupon) ? ($coupon['categories'] ?? null) : (is_object($coupon) ? $coupon->categories : null);
$coupon_amount = is_array($coupon) ? ($coupon['amount'] ?? 0) : (is_object($coupon) ? $coupon->amount : 0);
if ( $coupon_type )
{
if ( $coupon_categories != null )
{
$cats = is_string($coupon_categories) ? json_decode($coupon_categories) : $coupon_categories;
$product_categories = $productRepo->productCategories( $basket_position['parent_id'] ? $basket_position['parent_id'] : $basket_position['product-id'] );
if ( is_array( $cats ) ) foreach ( $cats as $category_tmp )
{
if ( in_array( $category_tmp, $product_categories ) )
{
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
break;
}
}
}
else
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $coupon_amount / 100 );
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
{
if ( $basket_position['discount_type'] == 3 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
if ( $basket_position['discount_type'] == 1 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $basket_position['discount_amount'] / 100 );
}
}
else
{
if ( $basket_position['discount_amount'] )
{
if ( $basket_position['discount_type'] == 3 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
if ( $basket_position['discount_type'] == 1 )
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $basket_position['discount_amount'] / 100 );
}
}
}
return $price;
}
}

View File

@@ -763,4 +763,43 @@ class CategoryRepository
return $fallback ? (string)$fallback : '';
}
public function getCategoryProductIds(int $categoryId): array
{
if ($categoryId <= 0) {
return [];
}
$result = $this->db->select('pp_shop_products_categories', 'product_id', ['category_id' => $categoryId]);
return is_array($result) ? $result : [];
}
public function subcategoriesLangCached(int $categoryId): array
{
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "subcategories_lang:{$categoryId}";
$cached = $cacheHandler->get($cacheKey);
if ($cached) {
return unserialize($cached);
}
$categories = $this->db->select('pp_shop_categories', '*', ['parent_id' => $categoryId]);
if (!is_array($categories)) {
return [];
}
$result = [];
foreach ($categories as $cat) {
$lang = $this->db->get('pp_shop_categories_langs', '*', ['category_id' => $cat['id']]);
if (is_array($lang)) {
$result[] = $lang;
}
}
$cacheHandler->set($cacheKey, $result);
return $result;
}
}

View File

@@ -555,22 +555,22 @@ class IntegrationsRepository
if ( !$accessToken )
return [ 'success' => false, 'message' => 'Brak tokenu Apilo.' ];
$product = new \shop\Product( $productId );
$product = ( new \Domain\Product\ProductRepository( $this->db ) )->findCached( $productId );
$params = [
'sku' => $product->sku,
'ean' => $product->ean,
'name' => $product->language['name'],
'tax' => (int) $product->vat,
'sku' => $product['sku'],
'ean' => $product['ean'],
'name' => $product['language']['name'],
'tax' => (int) $product['vat'],
'status' => 1,
'quantity' => (int) $product->quantity,
'priceWithTax' => $product->price_brutto,
'description' => $product->language['description'] . '<br>' . $product->language['short_description'],
'quantity' => (int) $product['quantity'],
'priceWithTax' => $product['price_brutto'],
'description' => $product['language']['description'] . '<br>' . $product['language']['short_description'],
'shortDescription' => '',
'images' => [],
];
foreach ( $product->images as $image )
foreach ( $product['images'] as $image )
$params['images'][] = "https://" . $_SERVER['HTTP_HOST'] . $image['src'];
$ch = curl_init( "https://projectpro.apilo.com/rest/api/warehouse/product/" );

View File

@@ -73,34 +73,90 @@ class OrderAdminService
public function changeStatus(int $orderId, int $status, bool $sendEmail): array
{
$order = new \shop\Order($orderId);
$response = $order->update_status($status, $sendEmail ? 1 : 0);
$order = $this->orders->findRawById($orderId);
if (!$order || (int)$order['status'] === $status) {
return ['result' => false];
}
return is_array($response) ? $response : ['result' => false];
$db = $this->orders->getDb();
if ($this->orders->updateOrderStatus($orderId, $status))
{
$this->orders->insertStatusHistory($orderId, $status, $sendEmail ? 1 : 0);
$response = ['result' => true];
if ($sendEmail)
{
$order['status'] = $status;
$response['email'] = $this->sendStatusChangeEmail($order);
}
// Apilo status sync
$this->syncApiloStatusIfNeeded($order, $status);
return $response;
}
return ['result' => false];
}
public function resendConfirmationEmail(int $orderId): bool
{
$order = new \shop\Order($orderId);
global $settings;
return (bool)$order->order_resend_confirmation_email();
$db = $this->orders->getDb();
$order = $this->orders->orderDetailsFrontend($orderId);
if (!$order || !$order['id']) {
return false;
}
$coupon = (int)$order['coupon_id'] ? (new \Domain\Coupon\CouponRepository($db))->find((int)$order['coupon_id']) : null;
$mail_order = \Shared\Tpl\Tpl::view('shop-order/mail-summary', [
'settings' => $settings,
'order' => $order,
'coupon' => $coupon,
]);
$settings['ssl'] ? $base = 'https' : $base = 'http';
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
$mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order);
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
$mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order);
\Shared\Helpers\Helpers::send_email($order['client_email'], \Shared\Helpers\Helpers::lang('potwierdzenie-zamowienia-ze-sklepu') . ' ' . $settings['firm_name'], $mail_order);
\Shared\Helpers\Helpers::send_email($settings['contact_email'], 'Nowe zamówienie / ' . $settings['firm_name'] . ' / ' . $order['number'] . ' - ' . $order['client_surname'] . ' ' . $order['client_name'], $mail_order);
return true;
}
public function setOrderAsUnpaid(int $orderId): bool
{
$order = new \shop\Order($orderId);
return (bool)$order->set_as_unpaid();
$this->orders->setAsUnpaid($orderId);
return true;
}
public function setOrderAsPaid(int $orderId, bool $sendMail): bool
{
$order = new \shop\Order($orderId);
if (!$order->set_as_paid()) {
$order = $this->orders->findRawById($orderId);
if (!$order) {
return false;
}
$order->update_status(4, $sendMail ? 1 : 0);
// Apilo payment sync
$this->syncApiloPaymentIfNeeded($order);
// Mark as paid
$this->orders->setAsPaid($orderId);
// Set status to 1 (opłacone) without email
$this->changeStatus($orderId, 1, false);
// Set status to 4 (przyjęte do realizacji) with email
$this->changeStatus($orderId, 4, $sendMail);
return true;
}
@@ -132,7 +188,7 @@ class OrderAdminService
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'id' => (int)$order['apilo_order_id'],
'status' => (int)( new \Domain\ShopStatus\ShopStatusRepository($this->db) )->getApiloStatusId( (int)$newStatus ),
'status' => (int)( new \Domain\ShopStatus\ShopStatusRepository($mdb) )->getApiloStatusId( (int)$newStatus ),
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $accessToken,
@@ -195,4 +251,331 @@ class OrderAdminService
{
return $this->orders->deleteOrder($orderId);
}
// =========================================================================
// Apilo sync queue (migrated from \shop\Order)
// =========================================================================
private const APILO_SYNC_QUEUE_FILE = '/temp/apilo-sync-queue.json';
public function processApiloSyncQueue(int $limit = 10): int
{
$queue = self::loadApiloSyncQueue();
if (!\Shared\Helpers\Helpers::is_array_fix($queue)) {
return 0;
}
$processed = 0;
foreach ($queue as $key => $task)
{
if ($processed >= $limit) {
break;
}
$order_id = (int)($task['order_id'] ?? 0);
if ($order_id <= 0) {
unset($queue[$key]);
continue;
}
$order = $this->orders->findRawById($order_id);
if (!$order) {
unset($queue[$key]);
continue;
}
$error = '';
$sync_failed = false;
$payment_pending = !empty($task['payment']) && (int)$order['paid'] === 1;
if ($payment_pending && (int)$order['apilo_order_id']) {
if (!$this->syncApiloPayment($order)) {
$sync_failed = true;
$error = 'payment_sync_failed';
}
}
$status_pending = isset($task['status']) && $task['status'] !== null && $task['status'] !== '';
if (!$sync_failed && $status_pending && (int)$order['apilo_order_id']) {
if (!$this->syncApiloStatus($order, (int)$task['status'])) {
$sync_failed = true;
$error = 'status_sync_failed';
}
}
if ($sync_failed) {
$task['attempts'] = (int)($task['attempts'] ?? 0) + 1;
$task['last_error'] = $error;
$task['updated_at'] = date('Y-m-d H:i:s');
$queue[$key] = $task;
} else {
unset($queue[$key]);
}
$processed++;
}
self::saveApiloSyncQueue($queue);
return $processed;
}
// =========================================================================
// Private: email
// =========================================================================
private function sendStatusChangeEmail(array $order): bool
{
if (!$order['client_email']) {
return false;
}
$db = $this->orders->getDb();
$order_statuses = $this->orders->orderStatuses();
$firm_name = (new \Domain\Settings\SettingsRepository($db))->getSingleValue('firm_name');
$status = (int)$order['status'];
$number = $order['number'];
$subjects = [
0 => $firm_name . ' - zamówienie [NUMER] zostało złożone',
1 => $firm_name . ' - zamówienie [NUMER] zostało opłacone',
2 => $firm_name . ' - płatność za zamówienie [NUMER] została odrzucona',
3 => $firm_name . ' - płatność za zamówienie [NUMER] jest sprawdzania ręcznie',
4 => $firm_name . ' - zamówienie [NUMER] zostało przyjęte do realizacji',
5 => $firm_name . ' - zamówienie [NUMER] zostało wysłane',
6 => $firm_name . ' - zamówienie [NUMER] zostało zrealizowane',
7 => $firm_name . ' - zamówienie [NUMER] zostało przygotowane go wysłania',
8 => $firm_name . ' - zamówienie [NUMER] zostało anulowane',
];
$subject = isset($subjects[$status]) ? str_replace('[NUMER]', $number, $subjects[$status]) : '';
if (!$subject) {
return false;
}
$email = new \Email(0);
$email->load_by_name('#sklep-zmiana-statusu-zamowienia');
$email->text = str_replace('[NUMER_ZAMOWIENIA]', $number, $email->text);
$email->text = str_replace('[DATA_ZAMOWIENIA]', date('Y/m/d', strtotime($order['date_order'])), $email->text);
$email->text = str_replace('[STATUS]', isset($order_statuses[$status]) ? $order_statuses[$status] : '', $email->text);
return $email->send($order['client_email'], $subject, true);
}
// =========================================================================
// Private: Apilo sync
// =========================================================================
private function syncApiloPaymentIfNeeded(array $order): void
{
global $config;
$db = $this->orders->getDb();
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
$apilo_settings = $integrationsRepository->getSettings('apilo');
if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) {
return;
}
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
self::appendApiloLog("SET AS PAID\n" . print_r($order, true));
}
if ($order['apilo_order_id'] && !$this->syncApiloPayment($order)) {
self::queueApiloSync((int)$order['id'], true, null, 'payment_sync_failed');
}
}
private function syncApiloStatusIfNeeded(array $order, int $status): void
{
global $config;
$db = $this->orders->getDb();
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
$apilo_settings = $integrationsRepository->getSettings('apilo');
if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) {
return;
}
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
self::appendApiloLog("UPDATE STATUS\n" . print_r($order, true));
}
if ($order['apilo_order_id'] && !$this->syncApiloStatus($order, $status)) {
self::queueApiloSync((int)$order['id'], false, $status, 'status_sync_failed');
}
}
private function syncApiloPayment(array $order): bool
{
global $config;
$db = $this->orders->getDb();
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
if (!(int)$order['apilo_order_id']) {
return true;
}
$payment_type = (int)(new \Domain\PaymentMethod\PaymentMethodRepository($db))->getApiloPaymentTypeId((int)$order['payment_method_id']);
if ($payment_type <= 0) {
$payment_type = 1;
}
$payment_date = new \DateTime($order['date_order']);
$access_token = $integrationsRepository->apiloGetAccessToken();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/payment/');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'amount' => str_replace(',', '.', $order['summary']),
'paymentDate' => $payment_date->format('Y-m-d\TH:i:s\Z'),
'type' => $payment_type,
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $access_token,
"Accept: application/json",
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$apilo_response = curl_exec($ch);
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_errno($ch) ? curl_error($ch) : '';
curl_close($ch);
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
self::appendApiloLog("PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_response, true));
}
if ($curl_error !== '') return false;
if ($http_code < 200 || $http_code >= 300) return false;
return true;
}
private function syncApiloStatus(array $order, int $status): bool
{
global $config;
$db = $this->orders->getDb();
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
if (!(int)$order['apilo_order_id']) {
return true;
}
$access_token = $integrationsRepository->apiloGetAccessToken();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/status/');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'id' => $order['apilo_order_id'],
'status' => (int)(new \Domain\ShopStatus\ShopStatusRepository($db))->getApiloStatusId($status),
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer " . $access_token,
"Accept: application/json",
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$apilo_result = curl_exec($ch);
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_errno($ch) ? curl_error($ch) : '';
curl_close($ch);
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
self::appendApiloLog("STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_result, true));
}
if ($curl_error !== '') return false;
if ($http_code < 200 || $http_code >= 300) return false;
return true;
}
// =========================================================================
// Private: Apilo sync queue file helpers
// =========================================================================
private static function queueApiloSync(int $order_id, bool $payment, ?int $status, string $error): void
{
if ($order_id <= 0) return;
$queue = self::loadApiloSyncQueue();
$key = (string)$order_id;
$row = is_array($queue[$key] ?? null) ? $queue[$key] : [];
$row['order_id'] = $order_id;
$row['payment'] = !empty($row['payment']) || $payment ? 1 : 0;
if ($status !== null) {
$row['status'] = $status;
}
$row['attempts'] = (int)($row['attempts'] ?? 0) + 1;
$row['last_error'] = $error;
$row['updated_at'] = date('Y-m-d H:i:s');
$queue[$key] = $row;
self::saveApiloSyncQueue($queue);
}
private static function apiloSyncQueuePath(): string
{
return dirname(__DIR__, 2) . self::APILO_SYNC_QUEUE_FILE;
}
private static function loadApiloSyncQueue(): array
{
$path = self::apiloSyncQueuePath();
if (!file_exists($path)) return [];
$content = file_get_contents($path);
if (!$content) return [];
$decoded = json_decode($content, true);
if (!is_array($decoded)) return [];
return $decoded;
}
private static function saveApiloSyncQueue(array $queue): void
{
$path = self::apiloSyncQueuePath();
$dir = dirname($path);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($path, json_encode($queue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
}
private static function appendApiloLog(string $message): void
{
$base = isset($_SERVER['DOCUMENT_ROOT']) && $_SERVER['DOCUMENT_ROOT']
? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\')
: dirname(__DIR__, 2);
$dir = $base . '/logs';
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents(
$dir . '/apilo.txt',
date('Y-m-d H:i:s') . ' --- ' . $message . "\n\n",
FILE_APPEND
);
}
}

View File

@@ -619,7 +619,8 @@ class OrderRepository
if (is_array($basket)) {
foreach ($basket as $basket_position) {
$attributes = '';
$product = \shop\Product::getFromCache($basket_position['product-id'], $lang_id);
$productRepo = new \Domain\Product\ProductRepository($this->db);
$product = $productRepo->findCached($basket_position['product-id'], $lang_id);
if (is_array($basket_position['attributes'])) {
foreach ($basket_position['attributes'] as $row) {
@@ -640,7 +641,7 @@ class OrderRepository
$product_custom_fields = '';
if (is_array($basket_position['custom_fields'])) {
foreach ($basket_position['custom_fields'] as $key => $val) {
$custom_field = \shop\ProductCustomField::getFromCache($key);
$custom_field = (new \Domain\Product\ProductRepository($this->db))->findCustomFieldCached($key);
if ($product_custom_fields) {
$product_custom_fields .= '<br>';
}
@@ -648,15 +649,15 @@ class OrderRepository
}
}
$product_price_tmp = \shop\Product::calculate_basket_product_price((float)$product['price_brutto_promo'], (float)$product['price_brutto'], $coupon, $basket_position);
$product_price_tmp = \Domain\Basket\BasketCalculator::calculateBasketProductPrice((float)$product['price_brutto_promo'], (float)$product['price_brutto'], $coupon, $basket_position);
$this->db->insert('pp_shop_order_products', [
'order_id' => $order_id,
'product_id' => $basket_position['product-id'],
'parent_product_id' => $basket_position['parent_id'] ? $basket_position['parent_id'] : $basket_position['product-id'],
'name' => $product->language['name'],
'name' => $product['language']['name'],
'attributes' => $attributes,
'vat' => $product->vat,
'vat' => $product['vat'],
'price_brutto' => $product_price_tmp['price'],
'price_brutto_promo' => $product_price_tmp['price_new'],
'quantity' => $basket_position['quantity'],
@@ -664,7 +665,7 @@ class OrderRepository
'custom_fields' => $product_custom_fields,
]);
$product_quantity = \shop\Product::get_product_quantity($basket_position['product-id']);
$product_quantity = $productRepo->getQuantity($basket_position['product-id']);
if ($product_quantity != null) {
$this->db->update('pp_shop_products', ['quantity[-]' => $basket_position['quantity']], ['id' => $basket_position['product-id']]);
} else {
@@ -700,13 +701,72 @@ class OrderRepository
// zmiana statusu w realizacji jeżeli płatność przy odbiorze
if ($payment_id == 3) {
$order_tmp = new \shop\Order($order_id);
$order_tmp->update_status(4, true);
$this->updateOrderStatus($order_id, 4);
$this->insertStatusHistory($order_id, 4, 1);
}
return $order_id;
}
// =========================================================================
// Low-level helpers (used by OrderAdminService)
// =========================================================================
public function getDb()
{
return $this->db;
}
public function findRawById(int $orderId): ?array
{
if ($orderId <= 0) return null;
$result = $this->db->get('pp_shop_orders', '*', ['id' => $orderId]);
return is_array($result) ? $result : null;
}
public function findRawByHash(string $hash): ?array
{
if ($hash === '') return null;
$result = $this->db->get('pp_shop_orders', '*', ['hash' => $hash]);
return is_array($result) ? $result : null;
}
public function findRawByPrzelewy24Hash(string $hash): ?array
{
if ($hash === '') return null;
$result = $this->db->get('pp_shop_orders', '*', ['przelewy24_hash' => $hash]);
return is_array($result) ? $result : null;
}
public function setAsPaid(int $orderId): void
{
$this->db->update('pp_shop_orders', ['paid' => 1], ['id' => $orderId]);
}
public function setAsUnpaid(int $orderId): void
{
$this->db->update('pp_shop_orders', ['paid' => 0], ['id' => $orderId]);
}
public function updateOrderStatus(int $orderId, int $status): bool
{
return (bool)$this->db->update('pp_shop_orders', ['status' => $status], ['id' => $orderId]);
}
public function insertStatusHistory(int $orderId, int $statusId, int $mail): void
{
$this->db->insert('pp_shop_order_statuses', [
'order_id' => $orderId,
'status_id' => $statusId,
'mail' => $mail,
]);
}
public function updateApiloStatusDate(int $orderId, string $date): void
{
$this->db->update('pp_shop_orders', ['apilo_order_status_date' => $date], ['id' => $orderId]);
}
private function nullableString(string $value): ?string
{
$value = trim($value);

View File

@@ -1218,7 +1218,7 @@ class ProductRepository
$vat = $this->db->get( 'pp_shop_products', 'vat', [ 'id' => $productId ] );
$attributeRepository = new \Domain\Attribute\AttributeRepository( $this->db );
$permutations = \shop\Product::array_cartesian( $attributes );
$permutations = self::arrayCartesian( $attributes );
if ( !\Shared\Helpers\Helpers::is_array_fix( $permutations ) ) {
return true;
}
@@ -1544,11 +1544,12 @@ class ProductRepository
if ( \Shared\Helpers\Helpers::is_array_fix( $rows ) ) {
foreach ( $rows as $productId ) {
$product = \shop\Product::getFromCache( $productId, $lang_id );
$product = $this->findCached( $productId, $lang_id );
if ( !$product ) continue;
if ( is_array( $product->product_combinations ) && count( $product->product_combinations ) ) {
foreach ( $product->product_combinations as $productCombination ) {
if ( $productCombination->quantity !== null || $productCombination->stock_0_buy ) {
if ( is_array( $product['product_combinations'] ) && count( $product['product_combinations'] ) ) {
foreach ( $product['product_combinations'] as $productCombination ) {
if ( $productCombination['quantity'] !== null || $productCombination['stock_0_buy'] ) {
$this->appendCombinationToXml( $doc, $channelNode, $product, $productCombination, $domainPrefix, $url );
}
}
@@ -1567,29 +1568,29 @@ class ProductRepository
private function appendCombinationToXml(\DOMDocument $doc, \DOMElement $channelNode, $product, $combination, string $domainPrefix, string $url): void
{
$itemNode = $channelNode->appendChild( $doc->createElement( 'item' ) );
$itemNode->appendChild( $doc->createElement( 'g:id', $combination->id ) );
$itemNode->appendChild( $doc->createElement( 'g:item_group_id', $product->id ) );
$itemNode->appendChild( $doc->createElement( 'g:id', $combination['id'] ) );
$itemNode->appendChild( $doc->createElement( 'g:item_group_id', $product['id'] ) );
for ( $l = 0; $l <= 4; $l++ ) {
$label = 'custom_label_' . $l;
if ( $product->$label ) {
$itemNode->appendChild( $doc->createElement( 'g:' . $label, $product->$label ) );
if ( isset( $product[$label] ) && $product[$label] ) {
$itemNode->appendChild( $doc->createElement( 'g:' . $label, $product[$label] ) );
}
}
$title = $product->language['xml_name'] ?: $product->language['name'];
$itemNode->appendChild( $doc->createElement( 'title', str_replace( '&', '&amp;', $title ) . ' - ' . $product->generateSubtitleFromAttributes( $combination->permutation_hash ) ) );
$title = $product['language']['xml_name'] ?: $product['language']['name'];
$itemNode->appendChild( $doc->createElement( 'title', str_replace( '&', '&amp;', $title ) . ' - ' . $this->generateSubtitleFromAttributes( $combination['permutation_hash'] ) ) );
$gtin = $product->ean ?: $this->generateEAN( $product->id );
$gtin = $product['ean'] ?: $this->generateEAN( $product['id'] );
$itemNode->appendChild( $doc->createElement( 'g:gtin', $gtin ) );
$desc = $product->language['short_description'] ?: $product->language['name'];
$desc = $product['language']['short_description'] ?: $product['language']['name'];
$itemNode->appendChild( $doc->createElement( 'g:description', html_entity_decode( strip_tags( $desc ) ) ) );
if ( $product->language['seo_link'] ) {
$link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product->language['seo_link'] ) . '/' . str_replace( '|', '/', $combination->permutation_hash );
if ( $product['language']['seo_link'] ) {
$link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product['language']['seo_link'] ) . '/' . str_replace( '|', '/', $combination['permutation_hash'] );
} else {
$link = $domainPrefix . '://' . $url . '/p-' . $product->id . '-' . \Shared\Helpers\Helpers::seo( $product->language['name'] ) . '/' . str_replace( '|', '/', $combination->permutation_hash );
$link = $domainPrefix . '://' . $url . '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] ) . '/' . str_replace( '|', '/', $combination['permutation_hash'] );
}
$itemNode->appendChild( $doc->createElement( 'link', $link ) );
@@ -1597,32 +1598,32 @@ class ProductRepository
$itemNode->appendChild( $doc->createElement( 'g:condition', 'new' ) );
if ( $combination->quantity !== null ) {
if ( $combination->quantity > 0 ) {
if ( $combination['quantity'] !== null ) {
if ( $combination['quantity'] > 0 ) {
$itemNode->appendChild( $doc->createElement( 'g:availability', 'in stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $combination->quantity ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $combination['quantity'] ) );
} else {
$itemNode->appendChild( $doc->createElement( 'g:availability', $combination->stock_0_buy ? 'in stock' : 'out of stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:availability', $combination['stock_0_buy'] ? 'in stock' : 'out of stock' ) );
}
} else {
if ( $product->quantity > 0 ) {
if ( $product['quantity'] > 0 ) {
$itemNode->appendChild( $doc->createElement( 'g:availability', 'in stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product->quantity ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product['quantity'] ) );
} else {
$itemNode->appendChild( $doc->createElement( 'g:availability', $product->stock_0_buy ? 'in stock' : 'out of stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product->stock_0_buy ? 999 : 0 ) );
$itemNode->appendChild( $doc->createElement( 'g:availability', $product['stock_0_buy'] ? 'in stock' : 'out of stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product['stock_0_buy'] ? 999 : 0 ) );
}
}
if ( $combination->price_brutto ) {
$itemNode->appendChild( $doc->createElement( 'g:price', $combination->price_brutto . ' PLN' ) );
if ( $combination->price_brutto_promo ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $combination->price_brutto_promo . ' PLN' ) );
if ( $combination['price_brutto'] ) {
$itemNode->appendChild( $doc->createElement( 'g:price', $combination['price_brutto'] . ' PLN' ) );
if ( $combination['price_brutto_promo'] ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $combination['price_brutto_promo'] . ' PLN' ) );
}
} else {
$itemNode->appendChild( $doc->createElement( 'g:price', $product->price_brutto . ' PLN' ) );
if ( $product->price_brutto_promo ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $product->price_brutto_promo . ' PLN' ) );
$itemNode->appendChild( $doc->createElement( 'g:price', $product['price_brutto'] . ' PLN' ) );
if ( $product['price_brutto_promo'] ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $product['price_brutto_promo'] . ' PLN' ) );
}
}
@@ -1635,33 +1636,33 @@ class ProductRepository
private function appendProductToXml(\DOMDocument $doc, \DOMElement $channelNode, $product, string $domainPrefix, string $url): void
{
$itemNode = $channelNode->appendChild( $doc->createElement( 'item' ) );
$itemNode->appendChild( $doc->createElement( 'g:id', $product->id ) );
$itemNode->appendChild( $doc->createElement( 'g:item_group_id', $product->id ) );
$itemNode->appendChild( $doc->createElement( 'g:id', $product['id'] ) );
$itemNode->appendChild( $doc->createElement( 'g:item_group_id', $product['id'] ) );
if ( $product->google_xml_label ) {
$itemNode->appendChild( $doc->createElement( 'g:custom_label_0', $product->google_xml_label ) );
if ( isset( $product['google_xml_label'] ) && $product['google_xml_label'] ) {
$itemNode->appendChild( $doc->createElement( 'g:custom_label_0', $product['google_xml_label'] ) );
}
$title = $product->language['xml_name'] ?: $product->language['name'];
$title = $product['language']['xml_name'] ?: $product['language']['name'];
$itemNode->appendChild( $doc->createElement( 'title', str_replace( '&', '&amp;', $title ) ) );
$gtin = $product->ean ?: $this->generateEAN( $product->id );
$gtin = $product['ean'] ?: $this->generateEAN( $product['id'] );
$itemNode->appendChild( $doc->createElement( 'g:gtin', $gtin ) );
$desc = $product->language['short_description'] ?: $product->language['name'];
$desc = $product['language']['short_description'] ?: $product['language']['name'];
$itemNode->appendChild( $doc->createElement( 'g:description', html_entity_decode( strip_tags( $desc ) ) ) );
if ( $product->language['seo_link'] ) {
$link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product->language['seo_link'] );
if ( $product['language']['seo_link'] ) {
$link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product['language']['seo_link'] );
} else {
$link = $domainPrefix . '://' . $url . '/p-' . $product->id . '-' . \Shared\Helpers\Helpers::seo( $product->language['name'] );
$link = $domainPrefix . '://' . $url . '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] );
}
$itemNode->appendChild( $doc->createElement( 'link', $link ) );
for ( $l = 0; $l <= 4; $l++ ) {
$label = 'custom_label_' . $l;
if ( $product->$label ) {
$itemNode->appendChild( $doc->createElement( 'g:' . $label, $product->$label ) );
if ( isset( $product[$label] ) && $product[$label] ) {
$itemNode->appendChild( $doc->createElement( 'g:' . $label, $product[$label] ) );
}
}
@@ -1669,11 +1670,11 @@ class ProductRepository
$itemNode->appendChild( $doc->createElement( 'g:condition', 'new' ) );
if ( $product->quantity ) {
if ( $product['quantity'] ) {
$itemNode->appendChild( $doc->createElement( 'g:availability', 'in stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product->quantity ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', $product['quantity'] ) );
} else {
if ( $product->stock_0_buy ) {
if ( $product['stock_0_buy'] ) {
$itemNode->appendChild( $doc->createElement( 'g:availability', 'in stock' ) );
$itemNode->appendChild( $doc->createElement( 'g:quantity', 999 ) );
} else {
@@ -1682,9 +1683,9 @@ class ProductRepository
}
}
$itemNode->appendChild( $doc->createElement( 'g:price', $product->price_brutto . ' PLN' ) );
if ( $product->price_brutto_promo ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $product->price_brutto_promo . ' PLN' ) );
$itemNode->appendChild( $doc->createElement( 'g:price', $product['price_brutto'] . ' PLN' ) );
if ( $product['price_brutto_promo'] ) {
$itemNode->appendChild( $doc->createElement( 'g:sale_price', $product['price_brutto_promo'] . ' PLN' ) );
}
$this->appendShippingToXml( $doc, $itemNode, $product );
@@ -1695,12 +1696,12 @@ class ProductRepository
*/
private function appendImagesToXml(\DOMDocument $doc, \DOMElement $itemNode, $product, string $domainPrefix, string $url): void
{
if ( isset( $product->images[0] ) ) {
$itemNode->appendChild( $doc->createElement( 'g:image_link', $domainPrefix . '://' . $url . $product->images[0]['src'] ) );
if ( isset( $product['images'][0] ) ) {
$itemNode->appendChild( $doc->createElement( 'g:image_link', $domainPrefix . '://' . $url . $product['images'][0]['src'] ) );
}
if ( count( $product->images ) > 1 ) {
for ( $i = 1; $i < count( $product->images ); ++$i ) {
$itemNode->appendChild( $doc->createElement( 'g:additional_image_link', $domainPrefix . '://' . $url . $product->images[$i]['src'] ) );
if ( is_array( $product['images'] ) && count( $product['images'] ) > 1 ) {
for ( $i = 1; $i < count( $product['images'] ); ++$i ) {
$itemNode->appendChild( $doc->createElement( 'g:additional_image_link', $domainPrefix . '://' . $url . $product['images'][$i]['src'] ) );
}
}
}
@@ -1714,7 +1715,7 @@ class ProductRepository
$shippingNode->appendChild( $doc->createElement( 'g:country', 'PL' ) );
$shippingNode->appendChild( $doc->createElement( 'g:service', '1 dzień roboczy' ) );
$shippingNode->appendChild( $doc->createElement( 'g:price',
( new \Domain\Transport\TransportRepository( $this->db ) )->lowestTransportPrice( (int) $product->wp ) . ' PLN'
( new \Domain\Transport\TransportRepository( $this->db ) )->lowestTransportPrice( (int) $product['wp'] ) . ' PLN'
) );
}
@@ -2243,4 +2244,603 @@ class ProductRepository
'AND' => ['product_id' => $productId, 'lang_id' => $langId],
]);
}
public function findCustomFieldCached(int $customFieldId)
{
if ($customFieldId <= 0) {
return null;
}
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = 'custom_field:' . $customFieldId;
$cached = $cacheHandler->get($cacheKey);
if ($cached) {
return unserialize($cached);
}
$result = $this->db->get('pp_shop_products_custom_fields', '*', ['id_additional_field' => $customFieldId]);
if (!is_array($result)) {
return null;
}
$cacheHandler->set($cacheKey, $result, 60 * 60 * 24);
return $result;
}
// =========================================================================
// Migrated from \shop\Product
// =========================================================================
/**
* Replacement for $this->findCached() — returns array instead of Product object.
* Uses same Redis cache key pattern: shop\product:{id}:{lang}:{hash}
*/
public function findCached(int $productId, string $langId = null, string $permutationHash = null)
{
if ($productId <= 0) {
return null;
}
if (!$langId) {
$langId = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
}
$cacheKey = "shop\\product:$productId:$langId:$permutationHash";
$cacheHandler = new \Shared\Cache\CacheHandler();
$cached = $cacheHandler->get($cacheKey);
if ($cached) {
$data = unserialize($cached);
// Legacy cache may contain old \shop\Product objects — invalidate and re-fetch
if (is_object($data)) {
$cacheHandler->delete($cacheKey);
// Fall through to re-fetch from DB
} elseif (is_array($data)) {
return $data;
}
}
$product = $this->db->get('pp_shop_products', '*', ['id' => $productId]);
if (!is_array($product)) {
return null;
}
$effectiveId = $productId;
// Combination — load data from parent
if ($product['parent_id']) {
$effectiveId = (int) $product['parent_id'];
if (!$product['price_netto'] || !$product['price_brutto']) {
$parentPrices = $this->db->get('pp_shop_products', ['price_netto', 'price_brutto', 'price_netto_promo', 'price_brutto_promo', 'vat', 'wp'], ['id' => $effectiveId]);
if (is_array($parentPrices)) {
foreach ($parentPrices as $k => $v) {
$product[$k] = $v;
}
}
}
}
// Language
$langRows = $this->db->select('pp_shop_products_langs', '*', ['AND' => ['product_id' => $effectiveId, 'lang_id' => $langId]]);
if (\Shared\Helpers\Helpers::is_array_fix($langRows)) {
foreach ($langRows as $row) {
if ($row['copy_from']) {
$copyRows = $this->db->select('pp_shop_products_langs', '*', ['AND' => ['product_id' => $effectiveId, 'lang_id' => $row['copy_from']]]);
if (is_array($copyRows)) {
foreach ($copyRows as $row2) {
$product['language'] = $row2;
}
}
} else {
$product['language'] = $row;
}
}
}
// Images, files, categories, related, sets
$product['images'] = $this->db->select('pp_shop_products_images', '*', ['product_id' => $effectiveId, 'ORDER' => ['o' => 'ASC', 'id' => 'ASC']]);
$product['files'] = $this->db->select('pp_shop_products_files', '*', ['product_id' => $effectiveId]);
$product['categories'] = $this->db->select('pp_shop_products_categories', 'category_id', ['product_id' => $effectiveId]);
$product['products_related'] = $this->db->select('pp_shop_products_related', 'product_related_id', ['product_id' => $effectiveId]);
$product['products_sets'] = $this->db->select('pp_shop_product_sets_products', 'product_id', ['AND' => ['set_id' => (int) ($product['set_id'] ?? 0), 'product_id[!]' => $effectiveId]]);
// Product combinations (only for main products)
if (!$product['parent_id']) {
$combIds = $this->db->select('pp_shop_products', 'id', ['parent_id' => $productId]);
if (\Shared\Helpers\Helpers::is_array_fix($combIds)) {
$combos = [];
foreach ($combIds as $combId) {
$combos[] = $this->findCached((int) $combId, $langId);
}
$product['product_combinations'] = $combos;
}
}
// Producer
$producer = $this->db->get('pp_shop_producer', '*', ['id' => (int) ($product['producer_id'] ?? 0)]);
$producerLang = $this->db->get('pp_shop_producer_lang', '*', ['AND' => ['producer_id' => (int) ($product['producer_id'] ?? 0), 'lang_id' => $langId]]);
if (is_array($producer)) {
$producer['description'] = is_array($producerLang) ? ($producerLang['description'] ?? null) : null;
$producer['data'] = is_array($producerLang) ? ($producerLang['data'] ?? null) : null;
$producer['meta_title'] = is_array($producerLang) ? ($producerLang['meta_title'] ?? null) : null;
}
$product['producer'] = $producer;
// Permutation price overrides
if ($permutationHash) {
$permPrices = $this->db->get('pp_shop_products', ['price_netto', 'price_brutto', 'price_netto_promo', 'price_brutto_promo'], ['AND' => ['permutation_hash' => $permutationHash, 'parent_id' => $productId]]);
if (is_array($permPrices)) {
if ($permPrices['price_netto'] !== null) $product['price_netto'] = $permPrices['price_netto'];
if ($permPrices['price_brutto'] !== null) $product['price_brutto'] = $permPrices['price_brutto'];
if ($permPrices['price_netto_promo'] !== null) $product['price_netto_promo'] = $permPrices['price_netto_promo'];
if ($permPrices['price_brutto_promo'] !== null) $product['price_brutto_promo'] = $permPrices['price_brutto_promo'];
}
}
// Custom fields
$product['custom_fields'] = $this->db->select('pp_shop_products_custom_fields', '*', ['id_product' => $productId]);
$cacheHandler->set($cacheKey, $product);
return $product;
}
public function isProductOnPromotion(int $productId): bool
{
return $this->db->get('pp_shop_products', 'price_netto_promo', ['id' => $productId]) !== null;
}
public function productSetsWhenAddToBasket(int $productId): string
{
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "ProductRepository::productSetsWhenAddToBasket:$productId";
$objectData = $cacheHandler->get($cacheKey);
if (!$objectData) {
$parentId = $this->db->get('pp_shop_products', 'parent_id', ['id' => $productId]);
if ($parentId) {
$setId = $this->db->get('pp_shop_products', 'set_id', ['id' => $parentId]);
} else {
$setId = $this->db->get('pp_shop_products', 'set_id', ['id' => $productId]);
}
$products = $this->db->select('pp_shop_product_sets_products', 'product_id', ['set_id' => $setId]);
if (!$products) {
$products_intersection = $this->db->select('pp_shop_orders_products_intersection', ['product_1_id', 'product_2_id'], ['OR' => ['product_1_id' => $productId, 'product_2_id' => $productId], 'ORDER' => ['count' => 'DESC'], 'LIMIT' => 5]);
if (!count($products_intersection)) {
$parentId2 = $this->db->get('pp_shop_products', 'parent_id', ['id' => $productId]);
$products_intersection = $this->db->select('pp_shop_orders_products_intersection', ['product_1_id', 'product_2_id'], ['OR' => ['product_1_id' => $parentId2, 'product_2_id' => $parentId2], 'ORDER' => ['count' => 'DESC'], 'LIMIT' => 5]);
}
$products = [];
foreach ($products_intersection as $pi) {
if ($pi['product_1_id'] != $productId) {
$products[] = $pi['product_1_id'];
} else {
$products[] = $pi['product_2_id'];
}
}
$products = array_unique($products);
}
$cacheHandler->set($cacheKey, $products);
} else {
$products = unserialize($objectData);
}
if (is_array($products)) {
foreach ($products as $k => $pid) {
if (!$this->isProductActiveCached((int) $pid)) {
unset($products[$k]);
}
}
}
return \Shared\Tpl\Tpl::view('shop-basket/alert-product-sets', [
'products' => $products,
]);
}
public function addVisit(int $productId): void
{
$this->db->update('pp_shop_products', ['visits[+]' => 1], ['id' => $productId]);
}
public function getProductImg(int $productId)
{
$rows = $this->db->select('pp_shop_products_images', 'src', ['product_id' => $productId, 'ORDER' => ['o' => 'ASC']]);
if (\Shared\Helpers\Helpers::is_array_fix($rows)) {
foreach ($rows as $row) {
return $row;
}
}
return null;
}
public function getProductUrl(int $productId): string
{
$langId = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
$langRows = $this->db->select('pp_shop_products_langs', '*', ['AND' => ['product_id' => $productId, 'lang_id' => $langId]]);
$language = null;
if (\Shared\Helpers\Helpers::is_array_fix($langRows)) {
foreach ($langRows as $row) {
if ($row['copy_from']) {
$copyRows = $this->db->select('pp_shop_products_langs', '*', ['AND' => ['product_id' => $productId, 'lang_id' => $row['copy_from']]]);
if (is_array($copyRows)) {
foreach ($copyRows as $row2) {
$language = $row2;
}
}
} else {
$language = $row;
}
}
}
if ($language && $language['seo_link']) {
return '/' . $language['seo_link'];
}
return '/p-' . $productId . '-' . \Shared\Helpers\Helpers::seo($language ? $language['name'] : '');
}
public function searchProductsByNameCount(string $query, string $langId): int
{
$results = $this->db->query('SELECT COUNT(0) AS c FROM ( '
. 'SELECT psp.id, '
. '( CASE '
. 'WHEN copy_from IS NULL THEN name '
. 'WHEN copy_from IS NOT NULL THEN ( '
. 'SELECT name FROM pp_shop_products_langs WHERE lang_id = pspl.copy_from AND product_id = psp.id '
. ') '
. 'END ) AS name '
. 'FROM pp_shop_products AS psp '
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
. 'WHERE status = 1 AND name LIKE :query AND lang_id = :lang_id '
. ') AS q1', [
':query' => '%' . $query . '%',
':lang_id' => $langId,
])->fetchAll(\PDO::FETCH_ASSOC);
return (int) ($results[0]['c'] ?? 0);
}
public function getProductsIdByName(string $query, string $langId, int $limit, int $from): array
{
$results = $this->db->query('SELECT psp.id, '
. '( CASE '
. 'WHEN copy_from IS NULL THEN name '
. 'WHEN copy_from IS NOT NULL THEN ( '
. 'SELECT name FROM pp_shop_products_langs WHERE lang_id = pspl.copy_from AND product_id = psp.id '
. ') '
. 'END ) AS name '
. 'FROM pp_shop_products AS psp '
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = psp.id '
. 'WHERE status = 1 AND name LIKE :query AND lang_id = :lang_id '
. 'ORDER BY name ASC '
. 'LIMIT ' . (int) $from . ',' . (int) $limit, [
':query' => '%' . $query . '%',
':lang_id' => $langId,
])->fetchAll(\PDO::FETCH_ASSOC);
$output = [];
if (is_array($results)) {
foreach ($results as $row) {
$output[] = $row['id'];
}
}
return $output;
}
public function searchProductsByName(string $query, string $langId, int $page = 0): array
{
$count = $this->searchProductsByNameCount($query, $langId);
$ls = ceil($count / 12);
if ($page < 1) {
$page = 1;
} elseif ($page > $ls) {
$page = (int) $ls;
}
$from = 12 * ($page - 1);
if ($from < 0) {
$from = 0;
}
return [
'products' => $this->getProductsIdByName($query, $langId, 12, $from),
'count' => $count,
'ls' => $ls,
];
}
public function searchProductByNameAjax(string $query, string $langId): array
{
$results = $this->db->query(
'SELECT product_id FROM pp_shop_products_langs AS pspl '
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspl.product_id '
. 'WHERE status = 1 AND lang_id = :lang_id AND LOWER(name) LIKE :query '
. 'ORDER BY visits DESC LIMIT 12',
[':query' => '%' . $query . '%', ':lang_id' => $langId]
)->fetchAll(\PDO::FETCH_ASSOC);
return is_array($results) ? $results : [];
}
public function isStock0Buy(int $productId)
{
$parentId = $this->db->get('pp_shop_products', 'parent_id', ['id' => $productId]);
if ($parentId) {
return $this->db->get('pp_shop_products', 'stock_0_buy', ['id' => $parentId]);
}
return $this->db->get('pp_shop_products', 'stock_0_buy', ['id' => $productId]);
}
public function getProductPermutationQuantityOptions(int $productId, $permutation)
{
global $settings;
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "ProductRepository::getProductPermutationQuantityOptions:v2:$productId:$permutation";
$objectData = $cacheHandler->get($cacheKey);
if ($objectData) {
return unserialize($objectData);
}
$result = [];
if ($this->db->count('pp_shop_products', ['AND' => ['parent_id' => $productId, 'permutation_hash' => $permutation]])) {
$result['quantity'] = $this->db->get('pp_shop_products', 'quantity', ['AND' => ['parent_id' => $productId, 'permutation_hash' => $permutation]]);
$result['stock_0_buy'] = $this->db->get('pp_shop_products', 'stock_0_buy', ['AND' => ['parent_id' => $productId, 'permutation_hash' => $permutation]]);
if ($result['quantity'] === null) {
$result['quantity'] = $this->db->get('pp_shop_products', 'quantity', ['id' => $productId]);
if ($result['stock_0_buy'] === null) {
$result['stock_0_buy'] = $this->db->get('pp_shop_products', 'stock_0_buy', ['id' => $productId]);
}
}
} else {
$result['quantity'] = $this->db->get('pp_shop_products', 'quantity', ['id' => $productId]);
$result['stock_0_buy'] = $this->db->get('pp_shop_products', 'stock_0_buy', ['id' => $productId]);
}
$result['messages'] = $this->db->get('pp_shop_products_langs', ['warehouse_message_zero', 'warehouse_message_nonzero'], ['AND' => ['product_id' => $productId, 'lang_id' => 'pl']]);
if (!isset($result['messages']['warehouse_message_zero']) || !$result['messages']['warehouse_message_zero']) {
$result['messages']['warehouse_message_zero'] = isset($settings['warehouse_message_zero_pl']) ? $settings['warehouse_message_zero_pl'] : '';
}
if (!isset($result['messages']['warehouse_message_nonzero']) || !$result['messages']['warehouse_message_nonzero']) {
$result['messages']['warehouse_message_nonzero'] = isset($settings['warehouse_message_nonzero_pl']) ? $settings['warehouse_message_nonzero_pl'] : '';
}
$cacheHandler->set($cacheKey, $result);
return $result;
}
public function getProductIdByAttributes(int $parentId, array $attributes)
{
return $this->db->get('pp_shop_products', 'id', ['AND' => ['parent_id' => $parentId, 'permutation_hash' => implode('|', $attributes)]]);
}
public function getProductPermutationHash(int $productId)
{
return $this->db->get('pp_shop_products', 'permutation_hash', ['id' => $productId]);
}
/**
* Build attribute list from product combinations (array of arrays with permutation_hash).
*/
public function getProductAttributes($products)
{
if (!is_array($products) || !count($products)) {
return false;
}
$attrRepo = new \Domain\Attribute\AttributeRepository($this->db);
$attributes = [];
foreach ($products as $product) {
$hash = is_array($product) ? ($product['permutation_hash'] ?? '') : (is_object($product) ? $product->permutation_hash : '');
$permutations = explode('|', $hash);
foreach ($permutations as $permutation) {
$parts = explode('-', $permutation);
if (count($parts) < 2) continue;
$value = [];
$value['id'] = $parts[1];
$value['is_default'] = $attrRepo->isValueDefault((int) $parts[1]);
if (array_search($parts[1], array_column($attributes, 'id')) === false) {
$attributes[$parts[0]][] = $value;
}
}
}
$attributes = \Shared\Helpers\Helpers::removeDuplicates($attributes, 'id');
$sorted = [];
foreach ($attributes as $key => $val) {
$row = [];
$row['id'] = $key;
$row['values'] = $val;
$sorted[$attrRepo->getAttributeOrder((int) $key)] = $row;
}
return $sorted;
}
public function generateSkuCode(): string
{
$skus = $this->db->select('pp_shop_products', 'sku', ['sku[~]' => 'PP-']);
$codes = [];
if (is_array($skus)) {
foreach ($skus as $sku) {
$codes[] = (int) substr($sku, 3);
}
}
if (!empty($codes)) {
return 'PP-' . str_pad(max($codes) + 1, 6, '0', STR_PAD_LEFT);
}
return 'PP-000001';
}
public function productMeta(int $productId): array
{
$result = $this->db->select(
'pp_shop_products_categories (ppc)',
['[>]pp_shop_categories_langs (pcl)' => ['ppc.category_id' => 'category_id']],
['pcl.title', 'pcl.seo_link'],
['ppc.product_id' => $productId]
);
return is_array($result) ? $result : [];
}
public function generateSubtitleFromAttributes(string $permutationHash, string $langId = null): string
{
if (!$langId) {
global $lang_id;
$langId = $lang_id;
}
$subtitle = '';
$attrRepo = new \Domain\Attribute\AttributeRepository($this->db);
$parts = explode('|', $permutationHash);
foreach ($parts as $part) {
$attr = explode('-', $part);
if (count($attr) < 2) continue;
if ($subtitle) {
$subtitle .= ', ';
}
$subtitle .= $attrRepo->getAttributeNameById((int) $attr[0], $langId) . ': ' . $attrRepo->getAttributeValueById((int) $attr[1], $langId);
}
return $subtitle;
}
public function getDefaultCombinationPrices(array $product): array
{
$prices = [];
if (!isset($product['product_combinations']) || !is_array($product['product_combinations'])) {
return $prices;
}
$permutationHash = '';
$attributes = $this->getProductAttributes($product['product_combinations']);
if (is_array($attributes)) {
foreach ($attributes as $attribute) {
foreach ($attribute['values'] as $value) {
if ($value['is_default']) {
if ($permutationHash) {
$permutationHash .= '|';
}
$permutationHash .= $attribute['id'] . '-' . $value['id'];
}
}
}
}
foreach ($product['product_combinations'] as $combo) {
$comboHash = is_array($combo) ? ($combo['permutation_hash'] ?? '') : (is_object($combo) ? $combo->permutation_hash : '');
if ($comboHash == $permutationHash) {
$prices['price_netto'] = is_array($combo) ? ($combo['price_netto'] ?? null) : $combo->price_netto;
$prices['price_brutto'] = is_array($combo) ? ($combo['price_brutto'] ?? null) : $combo->price_brutto;
$prices['price_netto_promo'] = is_array($combo) ? ($combo['price_netto_promo'] ?? null) : $combo->price_netto_promo;
$prices['price_brutto_promo'] = is_array($combo) ? ($combo['price_brutto_promo'] ?? null) : $combo->price_brutto_promo;
}
}
return $prices;
}
public function getProductDataBySelectedAttributes(array $product, string $selectedAttribute): array
{
global $settings;
$result = [];
if (isset($product['product_combinations']) && is_array($product['product_combinations'])) {
foreach ($product['product_combinations'] as $combo) {
$comboHash = is_array($combo) ? ($combo['permutation_hash'] ?? '') : (is_object($combo) ? $combo->permutation_hash : '');
if ($comboHash == $selectedAttribute) {
$comboQty = is_array($combo) ? ($combo['quantity'] ?? null) : $combo->quantity;
$comboS0B = is_array($combo) ? ($combo['stock_0_buy'] ?? null) : $combo->stock_0_buy;
if ($comboQty !== null || $comboS0B) {
$result['quantity'] = $comboQty;
$result['stock_0_buy'] = $comboS0B;
$result['price_netto'] = \Shared\Helpers\Helpers::shortPrice(is_array($combo) ? $combo['price_netto'] : $combo->price_netto);
$result['price_brutto'] = \Shared\Helpers\Helpers::shortPrice(is_array($combo) ? $combo['price_brutto'] : $combo->price_brutto);
$pnp = is_array($combo) ? ($combo['price_netto_promo'] ?? null) : $combo->price_netto_promo;
$pbp = is_array($combo) ? ($combo['price_brutto_promo'] ?? null) : $combo->price_brutto_promo;
$result['price_netto_promo'] = $pnp ? \Shared\Helpers\Helpers::shortPrice($pnp) : null;
$result['price_brutto_promo'] = $pbp ? \Shared\Helpers\Helpers::shortPrice($pbp) : null;
} else {
$result['quantity'] = $product['quantity'] ?? null;
$result['stock_0_buy'] = $product['stock_0_buy'] ?? null;
$result['price_netto'] = \Shared\Helpers\Helpers::shortPrice($product['price_netto'] ?? 0);
$result['price_brutto'] = \Shared\Helpers\Helpers::shortPrice($product['price_brutto'] ?? 0);
$result['price_netto_promo'] = ($product['price_netto_promo'] ?? null) ? \Shared\Helpers\Helpers::shortPrice($product['price_netto_promo']) : null;
$result['price_brutto_promo'] = ($product['price_brutto_promo'] ?? null) ? \Shared\Helpers\Helpers::shortPrice($product['price_brutto_promo']) : null;
}
}
}
}
$lang = isset($product['language']) ? $product['language'] : [];
$result['messages']['warehouse_message_zero'] = !empty($lang['warehouse_message_zero']) ? $lang['warehouse_message_zero'] : (isset($settings['warehouse_message_zero_pl']) ? $settings['warehouse_message_zero_pl'] : '');
$result['messages']['warehouse_message_nonzero'] = !empty($lang['warehouse_message_nonzero']) ? $lang['warehouse_message_nonzero'] : (isset($settings['warehouse_message_nonzero_pl']) ? $settings['warehouse_message_nonzero_pl'] : '');
$result['permutation_hash'] = $selectedAttribute;
return $result;
}
public function productCategories(int $productId): array
{
$result = $this->db->select('pp_shop_products_categories', 'category_id', ['product_id' => $productId]);
return is_array($result) ? $result : [];
}
public static function arrayCartesian(array $input): array
{
$result = [];
foreach ($input as $key => $values) {
if (empty($values)) {
continue;
}
if (empty($result)) {
foreach ($values as $value) {
$result[] = [$key => $value];
}
} else {
$append = [];
foreach ($result as &$product) {
$product[$key] = array_shift($values);
$copy = $product;
foreach ($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}
array_unshift($values, $product[$key]);
}
$result = array_merge($result, $append);
}
}
return $result;
}
}

View File

@@ -8,6 +8,16 @@ class PromotionRepository
private $db;
private ?string $defaultLangId = null;
public static $condition_type = [
1 => 'Rabat procentowy na produkty z kategorii 1 jeżeli w koszyku jest produkt z kategorii 2',
2 => 'Rabat procentowy na produkty z kategorii 1 i 2',
3 => 'Najtańszy produkt w koszyku (z wybranych kategorii) za X zł',
4 => 'Rabat procentowy na cały koszyk',
5 => 'Rabat procentowy na produkty z kategorii 1 lub 2',
];
public static $discount_type = [ 1 => 'Rabat procentowy' ];
public function __construct($db)
{
$this->db = $db;
@@ -413,13 +423,75 @@ class PromotionRepository
try {
$cache = new \Shared\Cache\CacheHandler();
if (method_exists($cache, 'delete')) {
$cache->delete('\shop\Promotion::get_active_promotions');
$cache->delete('PromotionRepository::getActivePromotions');
}
} catch (\Throwable $e) {
// Cache invalidation should not block save/delete.
}
}
public function getActivePromotions()
{
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheKey = "PromotionRepository::getActivePromotions";
$objectData = $cacheHandler->get( $cacheKey );
if ( !$objectData )
{
$results = $this->db->select( 'pp_shop_promotion', 'id', [ 'AND' => [ 'status' => 1, 'OR #date_from' => [ 'date_from' => null, 'date_from[<=]' => date( 'Y-m-d' ) ], 'OR #date_to' => [ 'date_to' => null, 'date_to[>=]' => date( 'Y-m-d' ) ] ], 'ORDER' => [ 'id' => 'DESC' ] ] );
$cacheHandler->set( $cacheKey, $results );
}
else
{
return unserialize( $objectData );
}
return $results;
}
public function findPromotion( $basket )
{
foreach ( $basket as $key => $val )
{
unset( $basket[$key]['discount_type'] );
unset( $basket[$key]['discount_amount'] );
unset( $basket[$key]['discount_include_coupon'] );
unset( $basket[$key]['include_product_promo'] );
}
$basket_tmp = $basket;
$results = $this->getActivePromotions();
if ( is_array( $results ) and count( $results ) )
{
foreach ( $results as $row )
{
$promotion = $this->find( (int)$row );
if ( $promotion['id'] <= 0 )
continue;
if ( $promotion['condition_type'] == 4 )
return $this->applyTypeWholeBasket( $basket_tmp, $promotion );
if ( $promotion['condition_type'] == 3 )
return $this->applyTypeCheapestProduct( $basket_tmp, $promotion );
if ( $promotion['condition_type'] == 5 )
return $this->applyTypeCategoriesOr( $basket_tmp, $promotion );
if ( $promotion['condition_type'] == 2 )
return $this->applyTypeCategoriesAnd( $basket_tmp, $promotion );
if ( $promotion['condition_type'] == 1 )
return $this->applyTypeCategoryCondition( $basket_tmp, $promotion );
}
}
return $basket;
}
// =========================================================================
// Frontend: basket promotion logic (migrated from front\factory\ShopPromotion)
// =========================================================================
@@ -433,17 +505,17 @@ class PromotionRepository
foreach ( $basket as $key => $val )
{
$product_promotion = \shop\Product::is_product_on_promotion( $val['product-id'] );
$product_promotion = (new \Domain\Product\ProductRepository($this->db))->isProductOnPromotion( $val['product-id'] );
if ( !$product_promotion or $product_promotion and $promotion->include_product_promo )
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
{
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
foreach ( $product_categories as $category_tmp )
{
$basket[$key]['discount_type'] = $promotion->discount_type;
$basket[$key]['discount_amount'] = $promotion->amount;
$basket[$key]['discount_include_coupon'] = $promotion->include_coupon;
$basket[$key]['include_product_promo'] = $promotion->include_product_promo;
$basket[$key]['discount_type'] = $promotion['discount_type'];
$basket[$key]['discount_amount'] = $promotion['amount'];
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
}
}
}
@@ -457,15 +529,15 @@ class PromotionRepository
{
$productRepo = new \Domain\Product\ProductRepository( $this->db );
$condition_1 = false;
$categories = json_decode( $promotion->categories );
$categories = $promotion['categories'];
if ( is_array( $categories ) and is_array( $categories ) )
{
foreach ( $basket as $key => $val )
{
$product_promotion = \shop\Product::is_product_on_promotion( $val['product-id'] );
$product_promotion = (new \Domain\Product\ProductRepository($this->db))->isProductOnPromotion( $val['product-id'] );
if ( !$product_promotion or $product_promotion and $promotion->include_product_promo )
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
{
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
foreach ( $product_categories as $category_tmp )
@@ -477,12 +549,12 @@ class PromotionRepository
}
}
if ( count( $condition_1 ) >= $promotion->min_product_count )
if ( count( $condition_1 ) >= $promotion['min_product_count'] )
{
$cheapest_position = false;
foreach ( $basket as $key => $val )
{
$price = \shop\Product::get_product_price( $val['product-id'] );
$price = (new \Domain\Product\ProductRepository($this->db))->getPrice( $val['product-id'] );
if ( !$cheapest_position or $cheapest_position['price'] > $price )
{
$cheapest_position['price'] = $price;
@@ -492,9 +564,9 @@ class PromotionRepository
$basket[$cheapest_position['key']]['quantity'] = 1;
$basket[$cheapest_position['key']]['discount_type'] = 3;
$basket[$cheapest_position['key']]['discount_amount'] = $promotion->price_cheapest_product;
$basket[$cheapest_position['key']]['discount_include_coupon'] = $promotion->include_coupon;
$basket[$cheapest_position['key']]['include_product_promo'] = $promotion->include_product_promo;
$basket[$cheapest_position['key']]['discount_amount'] = $promotion['price_cheapest_product'];
$basket[$cheapest_position['key']]['discount_include_coupon'] = $promotion['include_coupon'];
$basket[$cheapest_position['key']]['include_product_promo'] = $promotion['include_product_promo'];
}
return $basket;
@@ -506,24 +578,24 @@ class PromotionRepository
public function applyTypeCategoriesOr(array $basket, $promotion): array
{
$productRepo = new \Domain\Product\ProductRepository( $this->db );
$categories = json_decode( $promotion->categories );
$condition_categories = json_decode( $promotion->condition_categories );
$categories = $promotion['categories'];
$condition_categories = $promotion['condition_categories'];
foreach ( $basket as $key => $val )
{
$product_promotion = \shop\Product::is_product_on_promotion( $val['product-id'] );
$product_promotion = (new \Domain\Product\ProductRepository($this->db))->isProductOnPromotion( $val['product-id'] );
if ( !$product_promotion or $product_promotion and $promotion->include_product_promo )
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
{
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
foreach ( $product_categories as $category_tmp )
{
if ( in_array( $category_tmp['category_id'], $condition_categories ) or in_array( $category_tmp['category_id'], $categories ) )
{
$basket[$key]['discount_type'] = $promotion->discount_type;
$basket[$key]['discount_amount'] = $promotion->amount;
$basket[$key]['discount_include_coupon'] = $promotion->include_coupon;
$basket[$key]['include_product_promo'] = $promotion->include_product_promo;
$basket[$key]['discount_type'] = $promotion['discount_type'];
$basket[$key]['discount_amount'] = $promotion['amount'];
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
}
}
}
@@ -540,8 +612,8 @@ class PromotionRepository
$condition_1 = false;
$condition_2 = false;
$categories = json_decode( $promotion->categories );
$condition_categories = json_decode( $promotion->condition_categories );
$categories = $promotion['categories'];
$condition_categories = $promotion['condition_categories'];
if ( is_array( $condition_categories ) and is_array( $categories ) )
{
@@ -577,10 +649,10 @@ class PromotionRepository
{
if ( in_array( $category_tmp['category_id'], $categories ) or in_array( $category_tmp['category_id'], $condition_categories ) )
{
$basket[$key]['discount_type'] = $promotion->discount_type;
$basket[$key]['discount_amount'] = $promotion->amount;
$basket[$key]['discount_include_coupon'] = $promotion->include_coupon;
$basket[$key]['include_product_promo'] = $promotion->include_product_promo;
$basket[$key]['discount_type'] = $promotion['discount_type'];
$basket[$key]['discount_amount'] = $promotion['amount'];
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
}
}
}
@@ -595,8 +667,8 @@ class PromotionRepository
{
$productRepo = new \Domain\Product\ProductRepository( $this->db );
$condition = false;
$categories = json_decode( $promotion->categories );
$condition_categories = json_decode( $promotion->condition_categories );
$categories = $promotion['categories'];
$condition_categories = $promotion['condition_categories'];
if ( is_array( $condition_categories ) and is_array( $categories ) )
{
@@ -622,10 +694,10 @@ class PromotionRepository
{
if ( in_array( $category_tmp['category_id'], $categories ) )
{
$basket[$key]['discount_type'] = $promotion->discount_type;
$basket[$key]['discount_amount'] = $promotion->amount;
$basket[$key]['discount_include_coupon'] = $promotion->include_coupon;
$basket[$key]['include_product_promo'] = $promotion->include_product_promo;
$basket[$key]['discount_type'] = $promotion['discount_type'];
$basket[$key]['discount_amount'] = $promotion['amount'];
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
}
}
}