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:
@@ -36,7 +36,7 @@ composer test
|
||||
|
||||
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
||||
|
||||
Current suite: **610 tests, 1816 assertions**.
|
||||
Current suite: **610 tests, 1817 assertions**.
|
||||
|
||||
### Creating Updates
|
||||
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs.
|
||||
@@ -59,7 +59,7 @@ shopPRO/
|
||||
│ │ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
|
||||
│ │ ├── Controllers/ # DI controllers (\front\Controllers\)
|
||||
│ │ └── Views/ # Static views (\front\Views\)
|
||||
│ └── shop/ # Legacy shop classes (Product, Order, etc.)
|
||||
│ └── shop/ # EMPTY — all legacy classes migrated to Domain\
|
||||
├── admin/ # Admin panel
|
||||
│ ├── templates/ # Admin view templates
|
||||
│ └── layout/ # Admin CSS/JS/icons
|
||||
@@ -93,7 +93,7 @@ Custom autoloader in each entry point (not Composer autoload at runtime). Tries
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a — existing directory)
|
||||
- `\Shared\` → `autoload/Shared/`
|
||||
- `\front\` → `autoload/front/`
|
||||
- `\shop\` → `autoload/shop/`
|
||||
- ~~`\shop\`~~ → `autoload/shop/` (EMPTY — all classes migrated to `\Domain\`)
|
||||
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
|
||||
|
||||
### Domain-Driven Architecture (migration complete for admin + frontend)
|
||||
@@ -187,6 +187,7 @@ Before starting implementation, review current state of docs (see AGENTS.md for
|
||||
- `docs/PROJECT_STRUCTURE.md` — detailed project structure and module status
|
||||
- `docs/REFACTORING_PLAN.md` — Domain migration plan and status
|
||||
- `docs/FRONTEND_REFACTORING_PLAN.md` — frontend migration plan (mostly complete)
|
||||
- `docs/LEGACY_SHOP_REFACTORING_PLAN.md` — plan usunięcia 12 legacy klas z `autoload/shop/` (UKOŃCZONY — wszystkie 12 klas usunięte)
|
||||
- `docs/DATABASE_STRUCTURE.md` — full database schema
|
||||
- `docs/TESTING.md` — test suite status and history
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<? if ( \Shared\Helpers\Helpers::is_array_fix( $this -> best_sales_products ) ): foreach ( $this -> best_sales_products as $row ):?>
|
||||
<? $product = \shop\Product::getFromCache( (int)$row['parent_product_id'], ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() );?>
|
||||
<? $product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( (int)$row['parent_product_id'], ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() );?>
|
||||
<tr>
|
||||
<td>
|
||||
<?
|
||||
@@ -132,7 +132,7 @@
|
||||
echo '<img src="'. $product['images'][0]['src'] . '">';
|
||||
?>
|
||||
</td>
|
||||
<td><?= $product -> language['name'];?></td>
|
||||
<td><?= $product['language']['name'];?></td>
|
||||
<td class="text-center"><?= $row['quantity_summary'];?></td>
|
||||
<td class="text-right"><?= number_format( $row['sales'], 2, '.', " " );?> zł</td>
|
||||
</tr>
|
||||
@@ -158,7 +158,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<? if ( \Shared\Helpers\Helpers::is_array_fix( $this -> most_view_products ) ): foreach ( $this -> most_view_products as $row ):?>
|
||||
<? $product = new \shop\Product( $row['id'] );?>
|
||||
<? $product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( $row['id'] );?>
|
||||
<tr>
|
||||
<td>
|
||||
<?
|
||||
|
||||
@@ -73,7 +73,7 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
||||
<div class="panel-body">
|
||||
<div>Kwota zamówienia <b><?= $this -> order[ 'summary' ];?> zł</b></div>
|
||||
<? if ( $this -> coupon ):?>
|
||||
<div>Kod rabatowy: <span style="color: #cc0000;"><?= $this -> coupon -> name;?> - <?= $this -> coupon -> amount;?> <?= $this -> coupon -> type == 1 ? '%' : 'zł';?></span></div>
|
||||
<div>Kod rabatowy: <span style="color: #cc0000;"><?= $this -> coupon['name'];?> - <?= $this -> coupon['amount'];?> <?= $this -> coupon['type'] == 1 ? '%' : 'zł';?></span></div>
|
||||
<? endif;?>
|
||||
<br>
|
||||
<div><?= strip_tags( $this -> order[ 'transport' ] );?>: <b><?= $this -> order[ 'transport_cost' ];?> zł</b></div>
|
||||
@@ -168,11 +168,11 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
||||
<tr class="order-product-details">
|
||||
<td class="product-image">
|
||||
<? if ( $product['product_id'] ):?>
|
||||
<img src="<?= \shop\Product::getProductImg( (int)$product_id );?>">
|
||||
<img src="<?= ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductImg( (int)$product_id );?>">
|
||||
<? endif;?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?= \shop\Product::getProductUrl( (int)$product_id );?>" target="_blank"><?= $product[ 'name' ];?></a>
|
||||
<a href="<?= ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductUrl( (int)$product_id );?>" target="_blank"><?= $product[ 'name' ];?></a>
|
||||
<br />
|
||||
<div class="atributes">
|
||||
<?= $product[ 'attributes' ];?>
|
||||
|
||||
@@ -177,11 +177,11 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
||||
<tr class="order-product-details">
|
||||
<td class="product-image">
|
||||
<? if ( $product['product_id'] ):?>
|
||||
<img src="<?= \shop\Product::getProductImg( (int)$product['product_id'] );?>">
|
||||
<img src="<?= ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductImg( (int)$product['product_id'] );?>">
|
||||
<? endif;?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?= \shop\Product::getProductUrl( (int)$product['product_id'] );?>" target="_blank"><?= $product[ 'name' ];?></a>
|
||||
<a href="<?= ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductUrl( (int)$product['product_id'] );?>" target="_blank"><?= $product[ 'name' ];?></a>
|
||||
<br />
|
||||
<div class="atributes">
|
||||
<?= $product[ 'attributes' ];?>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
$attributes = explode( '|', $product['permutation_hash'] );
|
||||
foreach ( $attributes as $attribute ):
|
||||
$attribute_tmp = explode( '-', $attribute );
|
||||
echo \shop\ProductAttribute::getAttributeName( (int)$attribute_tmp[0], $this -> default_language ) . ' - <b>' . \shop\ProductAttribute::get_value_name( (int)$attribute_tmp[1], $this -> default_language ) . '</b>';
|
||||
$attrRepo = new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] );
|
||||
echo $attrRepo->getAttributeNameById( (int)$attribute_tmp[0], $this -> default_language ) . ' - <b>' . $attrRepo->getAttributeValueById( (int)$attribute_tmp[1], $this -> default_language ) . '</b>';
|
||||
if ( $attribute != end( $attributes ) )
|
||||
echo ', ';
|
||||
endforeach;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/" );
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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( '&', '&', $title ) . ' - ' . $product->generateSubtitleFromAttributes( $combination->permutation_hash ) ) );
|
||||
$title = $product['language']['xml_name'] ?: $product['language']['name'];
|
||||
$itemNode->appendChild( $doc->createElement( 'title', str_replace( '&', '&', $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( '&', '&', $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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ class Helpers
|
||||
// Wyczyść cache produktu dla wszystkich języków i permutacji
|
||||
$cacheHandler -> deletePattern( "shop\\product:$product_id:*" );
|
||||
// Wyczyść cache związane z opcjami ilościowymi
|
||||
$cacheHandler -> deletePattern( "\\shop\\Product::get_product_permutation_quantity_options:$product_id:*" );
|
||||
$cacheHandler -> deletePattern( "ProductRepository::getProductPermutationQuantityOptions:v2:$product_id:*" );
|
||||
// Wyczyść cache zestawów produktów
|
||||
$cacheHandler -> deletePattern( "\\shop\\Product::product_sets_when_add_to_basket:$product_id" );
|
||||
$cacheHandler -> deletePattern( "ProductRepository::productSetsWhenAddToBasket:$product_id" );
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
@@ -956,5 +956,20 @@ class Helpers
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function shortPrice( $price )
|
||||
{
|
||||
if ( self::isWholeNumber( $price ) )
|
||||
$price = round( $price, 0 );
|
||||
else
|
||||
$price = self::decimal( $price );
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
public static function isWholeNumber( $value )
|
||||
{
|
||||
return ( is_numeric( $value ) && ( round( $value, 3 ) == round( $value ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class ShopOrderController
|
||||
|
||||
$coupon = null;
|
||||
if (!empty($order) && !empty($order['coupon_id'])) {
|
||||
$coupon = new \shop\Coupon((int)$order['coupon_id']);
|
||||
$coupon = ( new \Domain\Coupon\CouponRepository( $GLOBALS['mdb'] ) )->find((int)$order['coupon_id']);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-order/order-details', [
|
||||
@@ -191,7 +191,7 @@ class ShopOrderController
|
||||
return \Shared\Tpl\Tpl::view('shop-order/order-edit', [
|
||||
'order' => $this->service->details($orderId),
|
||||
'order_statuses' => $this->service->statuses(),
|
||||
'transport' => \shop\Transport::transport_list(),
|
||||
'transport' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->allActive(),
|
||||
'payment_methods' => ( new \Domain\PaymentMethod\PaymentMethodRepository( $GLOBALS['mdb'] ) )->allActive(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ class ShopProductController
|
||||
$categories = ( new CategoryRepository( $db ) )->subcategories( null );
|
||||
$layouts = $this->layoutsForProductEdit( $db );
|
||||
$products = $this->repository->allProductsList();
|
||||
$sets = \shop\ProductSet::sets_list();
|
||||
$sets = ( new \Domain\ProductSet\ProductSetRepository( $db ) )->allSets();
|
||||
$producers = ( new \Domain\Producer\ProducerRepository( $db ) )->allProducers();
|
||||
$units = ( new \Domain\Dictionaries\DictionariesRepository( $db ) )->allUnits();
|
||||
$dlang = $this->languagesRepository->defaultLanguage();
|
||||
@@ -920,7 +920,7 @@ class ShopProductController
|
||||
*/
|
||||
public function ajax_product_url(): void
|
||||
{
|
||||
echo json_encode( [ 'url' => \shop\Product::getProductUrl( (int) \Shared\Helpers\Helpers::get( 'product_id' ) ) ] );
|
||||
echo json_encode( [ 'url' => ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductUrl( (int) \Shared\Helpers\Helpers::get( 'product_id' ) ) ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -931,7 +931,7 @@ class ShopProductController
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas generowania kodu sku wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
$sku = \shop\Product::generate_sku_code( (int) \Shared\Helpers\Helpers::get( 'product_id' ) );
|
||||
$sku = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->generateSkuCode();
|
||||
if ( $sku ) {
|
||||
$response = [ 'status' => 'ok', 'sku' => $sku ];
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class ShopPromotionController
|
||||
'lp' => $lp++ . '.',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'name' => '<a href="/admin/shop_promotion/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'condition_type' => htmlspecialchars((string)(\shop\Promotion::$condition_type[$conditionType] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
||||
'condition_type' => htmlspecialchars((string)(\Domain\Promotion\PromotionRepository::$condition_type[$conditionType] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
||||
'date_from' => $dateFrom !== '' ? htmlspecialchars($dateFrom, ENT_QUOTES, 'UTF-8') : '-',
|
||||
'date_to' => $dateTo !== '' ? htmlspecialchars($dateTo, ENT_QUOTES, 'UTF-8') : '-',
|
||||
'_actions' => [
|
||||
@@ -248,13 +248,13 @@ class ShopPromotionController
|
||||
FormField::select('condition_type', [
|
||||
'label' => 'Warunki promocji',
|
||||
'tab' => 'settings',
|
||||
'options' => \shop\Promotion::$condition_type,
|
||||
'options' => \Domain\Promotion\PromotionRepository::$condition_type,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::select('discount_type', [
|
||||
'label' => 'Typ rabatu',
|
||||
'tab' => 'settings',
|
||||
'options' => \shop\Promotion::$discount_type,
|
||||
'options' => \Domain\Promotion\PromotionRepository::$discount_type,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::text('min_product_count', [
|
||||
|
||||
@@ -47,7 +47,7 @@ class App
|
||||
// wyświetlenie pojedynczego produktu
|
||||
if ( $product )
|
||||
{
|
||||
\shop\Product::add_visit( $product -> id );
|
||||
( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->addVisit( (int)$product['id'] );
|
||||
return \Shared\Tpl\Tpl::view( 'shop-product/product', [
|
||||
'product' => $product,
|
||||
'settings' => $settings,
|
||||
@@ -76,18 +76,6 @@ class App
|
||||
// stare klasy
|
||||
$class = '\front\controls\\' . $moduleName;
|
||||
|
||||
if ( class_exists( $class ) and method_exists( new $class, $action ) )
|
||||
return call_user_func_array( array( $class, $action ), array() );
|
||||
|
||||
// klasy sklepowe
|
||||
$class = '\shop\\';
|
||||
|
||||
$results = explode( '_', \Shared\Helpers\Helpers::get( 'module' ) );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$class .= ucfirst( $row );
|
||||
|
||||
$action = \Shared\Helpers\Helpers::get( 'action' );
|
||||
|
||||
if ( class_exists( $class ) and method_exists( new $class, $action ) )
|
||||
return call_user_func_array( array( $class, $action ), array() );
|
||||
|
||||
@@ -188,8 +176,10 @@ class App
|
||||
},
|
||||
'ShopOrder' => function() {
|
||||
global $mdb;
|
||||
$orderRepo = new \Domain\Order\OrderRepository( $mdb );
|
||||
return new \front\Controllers\ShopOrderController(
|
||||
new \Domain\Order\OrderRepository( $mdb )
|
||||
$orderRepo,
|
||||
new \Domain\Order\OrderAdminService( $orderRepo )
|
||||
);
|
||||
},
|
||||
'ShopProducer' => function() {
|
||||
@@ -204,6 +194,9 @@ class App
|
||||
new \Domain\Category\CategoryRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Search' => function() {
|
||||
return new \front\Controllers\SearchController();
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
49
autoload/front/Controllers/SearchController.php
Normal file
49
autoload/front/Controllers/SearchController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace front\Controllers;
|
||||
|
||||
class SearchController
|
||||
{
|
||||
public function searchResults()
|
||||
{
|
||||
global $lang_id;
|
||||
|
||||
$bs = \Shared\Helpers\Helpers::get( 'bs' );
|
||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||
$results = $productRepo->searchProductsByName( \Shared\Helpers\Helpers::get( 'query' ), $lang_id, (int)$bs );
|
||||
|
||||
$out = \Shared\Tpl\Tpl::view( 'shop-search/products', [
|
||||
'query' => \Shared\Helpers\Helpers::get( 'query' ),
|
||||
'products' => $results['products']
|
||||
] );
|
||||
|
||||
if ( $results['ls'] > 1 )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> ls = $results['ls'];
|
||||
$tpl -> bs = $bs ? $bs : 1;
|
||||
$tpl -> link = 'wyszukiwarka/' . \Shared\Helpers\Helpers::get( 'query' );
|
||||
$out .= $tpl -> render( 'site/pager' );
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function searchProducts()
|
||||
{
|
||||
global $lang_id;
|
||||
|
||||
$products = [];
|
||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||
$results = $productRepo->searchProductByNameAjax( \Shared\Helpers\Helpers::get( 'query' ), $lang_id );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) {
|
||||
foreach ( $results as $row ) {
|
||||
$products[] = \Shared\Tpl\Tpl::view( 'shop-search/product-search', [
|
||||
'product' => $productRepo->findCached( $row['product_id'], $lang_id )
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode( $products );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class ShopBasketController
|
||||
|
||||
unset( $basket[ $product_hash ] );
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
|
||||
|
||||
@@ -51,11 +51,11 @@ class ShopBasketController
|
||||
$basket_transport_method_id = \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' );
|
||||
$basket[ $product_hash ][ 'quantity' ]++;
|
||||
|
||||
\shop\Basket::check_product_quantity_in_stock( $basket, false );
|
||||
\Domain\Basket\BasketCalculator::checkProductQuantityInStock( $basket, false );
|
||||
|
||||
$basket = \Shared\Helpers\Helpers::get_session( 'basket' );
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
|
||||
|
||||
@@ -76,7 +76,7 @@ class ShopBasketController
|
||||
if ( $basket[ $product_hash ][ 'quantity' ] < 1 )
|
||||
unset( $basket[ $product_hash ] );
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
|
||||
|
||||
@@ -97,9 +97,9 @@ class ShopBasketController
|
||||
if ( $basket[ $product_hash ][ 'quantity' ] < 1 )
|
||||
unset( $basket[ $product_hash ] );
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
\shop\Basket::check_product_quantity_in_stock( $basket, false );
|
||||
\Domain\Basket\BasketCalculator::checkProductQuantityInStock( $basket, false );
|
||||
|
||||
$basket = \Shared\Helpers\Helpers::get_session( 'basket' );
|
||||
|
||||
@@ -116,7 +116,7 @@ class ShopBasketController
|
||||
|
||||
public function basketAddProduct()
|
||||
{
|
||||
$basket = \shop\Basket::validate_basket( \Shared\Helpers\Helpers::get_session( 'basket' ) );
|
||||
$basket = \Domain\Basket\BasketCalculator::validateBasket( \Shared\Helpers\Helpers::get_session( 'basket' ) );
|
||||
$values_tmp = json_decode( \Shared\Helpers\Helpers::get( 'values' ), true );
|
||||
$values = [];
|
||||
$attributes = [];
|
||||
@@ -144,7 +144,7 @@ class ShopBasketController
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $attributes ) )
|
||||
{
|
||||
$values['parent_id'] = $values[ 'product-id' ];
|
||||
$values['product-id'] = \shop\Product::get_product_id_by_attributes( $values[ 'product-id' ], $attributes );
|
||||
$values['product-id'] = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getProductIdByAttributes( $values[ 'product-id' ], $attributes );
|
||||
$values['attributes'] = $attributes;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class ShopBasketController
|
||||
$basket[ $product_code ]['message'] = $values['product-message'];
|
||||
$basket[ $product_code ]['custom_fields'] = $custom_fields;
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
|
||||
|
||||
@@ -174,7 +174,7 @@ class ShopBasketController
|
||||
'result' => 'ok',
|
||||
'basket_mini_count' => \Domain\Basket\BasketCalculator::countProductsText( \Domain\Basket\BasketCalculator::countProducts( $basket ) ),
|
||||
'basket_mini_value' => \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ),
|
||||
'product_sets' => \shop\Product::product_sets_when_add_to_basket( (int)$values['product-id'] )
|
||||
'product_sets' => ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->productSetsWhenAddToBasket( (int)$values['product-id'] )
|
||||
] );
|
||||
exit;
|
||||
}
|
||||
@@ -261,7 +261,7 @@ class ShopBasketController
|
||||
{
|
||||
global $lang_id, $settings;
|
||||
|
||||
if ( \shop\Basket::check_product_quantity_in_stock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) )
|
||||
if ( \Domain\Basket\BasketCalculator::checkProductQuantityInStock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) )
|
||||
{
|
||||
header( 'Location: /koszyk' );
|
||||
exit;
|
||||
@@ -286,7 +286,7 @@ class ShopBasketController
|
||||
{
|
||||
$client = \Shared\Helpers\Helpers::get_session( 'client' );
|
||||
|
||||
if ( \shop\Basket::check_product_quantity_in_stock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) )
|
||||
if ( \Domain\Basket\BasketCalculator::checkProductQuantityInStock( \Shared\Helpers\Helpers::get_session( 'basket' ) ) )
|
||||
{
|
||||
header( 'Location: /koszyk' );
|
||||
exit;
|
||||
@@ -357,13 +357,13 @@ class ShopBasketController
|
||||
$payment_method_id = \Shared\Helpers\Helpers::get_session( 'payment_method_id' );
|
||||
$basket_transport_method_id = \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' );
|
||||
|
||||
if ( \shop\Basket::check_product_quantity_in_stock( $basket ) )
|
||||
if ( \Domain\Basket\BasketCalculator::checkProductQuantityInStock( $basket ) )
|
||||
{
|
||||
header( 'Location: /koszyk' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$basket = \shop\Promotion::find_promotion( $basket );
|
||||
$basket = (new \Domain\Promotion\PromotionRepository($GLOBALS['mdb']))->findPromotion( $basket );
|
||||
|
||||
return \Shared\Tpl\Tpl::view( 'shop-basket/basket', [
|
||||
'basket' => $basket,
|
||||
|
||||
@@ -140,7 +140,7 @@ class ShopClientController
|
||||
return \front\Views\ShopClient::clientOrders([
|
||||
'client' => $client,
|
||||
'orders' => $this->clientRepo->clientOrders((int)$client['id']),
|
||||
'statuses' => \shop\Order::order_statuses(),
|
||||
'statuses' => ( new \Domain\Order\OrderRepository( $GLOBALS['mdb'] ) )->orderStatuses(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
namespace front\Controllers;
|
||||
|
||||
use Domain\Order\OrderRepository;
|
||||
use Domain\Order\OrderAdminService;
|
||||
|
||||
class ShopOrderController
|
||||
{
|
||||
private $repository;
|
||||
private $adminService;
|
||||
|
||||
public function __construct( OrderRepository $repository )
|
||||
public function __construct( OrderRepository $repository, OrderAdminService $adminService )
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->adminService = $adminService;
|
||||
}
|
||||
|
||||
public function paymentConfirmation()
|
||||
@@ -30,12 +33,11 @@ class ShopOrderController
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get( 'tr_status' ) == 'TRUE' && \Shared\Helpers\Helpers::get( 'tr_crc' ) )
|
||||
{
|
||||
$order = new \shop\Order( 0, \Shared\Helpers\Helpers::get( 'tr_crc' ) );
|
||||
$order = $this->repository->findRawByHash( \Shared\Helpers\Helpers::get( 'tr_crc' ) );
|
||||
|
||||
if ( $order->id )
|
||||
if ( $order && $order['id'] )
|
||||
{
|
||||
$order->set_as_paid( true );
|
||||
$order->update_status( 4, true );
|
||||
$this->adminService->setOrderAsPaid( (int)$order['id'], true );
|
||||
echo 'TRUE';
|
||||
exit;
|
||||
}
|
||||
@@ -68,14 +70,13 @@ class ShopOrderController
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $post ) );
|
||||
$response = curl_exec( $ch );
|
||||
|
||||
$order = new \shop\Order( 0, '', \Shared\Helpers\Helpers::get( 'p24_session_id' ) );
|
||||
$order = $this->repository->findRawByPrzelewy24Hash( \Shared\Helpers\Helpers::get( 'p24_session_id' ) );
|
||||
|
||||
if ( $order['status'] == 0 && $order['summary'] * 100 == \Shared\Helpers\Helpers::get( 'p24_amount' ) )
|
||||
if ( $order && $order['status'] == 0 && $order['summary'] * 100 == \Shared\Helpers\Helpers::get( 'p24_amount' ) )
|
||||
{
|
||||
if ( $order['id'] )
|
||||
{
|
||||
$order->set_as_paid( true );
|
||||
$order->update_status( 4, true );
|
||||
$this->adminService->setOrderAsPaid( (int)$order['id'], true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,9 +89,9 @@ class ShopOrderController
|
||||
|
||||
if ( !empty( $_POST["KWOTA"] ) && !empty( $_POST["ID_PLATNOSCI"] ) && !empty( $_POST["ID_ZAMOWIENIA"] ) && !empty( $_POST["STATUS"] ) && !empty( $_POST["SEKRET"] ) && !empty( $_POST["HASH"] ) )
|
||||
{
|
||||
$order = new \shop\Order( $_POST['ID_ZAMOWIENIA'] );
|
||||
$order = $this->repository->orderDetailsFrontend( (int)$_POST['ID_ZAMOWIENIA'] );
|
||||
|
||||
if ( $order['id'] )
|
||||
if ( $order && $order['id'] )
|
||||
{
|
||||
if ( is_array( $order['products'] ) && count( $order['products'] ) ):
|
||||
$summary_tmp = 0;
|
||||
@@ -105,21 +106,20 @@ class ShopOrderController
|
||||
{
|
||||
if ( $_POST["STATUS"] == "SUCCESS" )
|
||||
{
|
||||
$order->set_as_paid( true );
|
||||
$order->update_status( 4, true );
|
||||
$this->adminService->setOrderAsPaid( (int)$order['id'], true );
|
||||
|
||||
echo \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-oplacone' );
|
||||
}
|
||||
else if ( $_POST["STATUS"] == "FAILURE" )
|
||||
{
|
||||
$order->update_status( 2, true );
|
||||
$this->adminService->changeStatus( (int)$order['id'], 2, true );
|
||||
|
||||
echo \Shared\Helpers\Helpers::lang( 'platnosc-zostala-odrzucona' );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$order->update_status( 3, true );
|
||||
$this->adminService->changeStatus( (int)$order['id'], 3, true );
|
||||
|
||||
echo \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-oplacone-reczne' );
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class ShopOrderController
|
||||
$order = $this->repository->orderDetailsFrontend(
|
||||
$this->repository->findIdByHash( \Shared\Helpers\Helpers::get( 'order_hash' ) )
|
||||
);
|
||||
$coupon = (int)$order['coupon_id'] ? new \shop\Coupon( (int)$order['coupon_id'] ) : null;
|
||||
$coupon = (int)$order['coupon_id'] ? ( new \Domain\Coupon\CouponRepository( $GLOBALS['mdb'] ) )->find( (int)$order['coupon_id'] ) : null;
|
||||
|
||||
return \Shared\Tpl\Tpl::view( 'shop-order/order-details', [
|
||||
'order' => $order,
|
||||
|
||||
@@ -24,9 +24,10 @@ class ShopProductController
|
||||
(int)\Shared\Helpers\Helpers::get( 'offset' )
|
||||
);
|
||||
|
||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||
if ( is_array( $products_ids ) ): foreach ( $products_ids as $product_id ):
|
||||
$output .= \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( $product_id, $lang_id )
|
||||
'product' => $productRepo->findCached( $product_id, $lang_id )
|
||||
] );
|
||||
endforeach;
|
||||
endif;
|
||||
@@ -48,7 +49,29 @@ class ShopProductController
|
||||
$attributes[] = $val;
|
||||
}
|
||||
|
||||
$result = \shop\Product::getWarehouseMessage( $values['product-id'], $attributes, $lang_id );
|
||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||
$permutation = self::getPermutation( $attributes );
|
||||
$quantity = self::getPermutationQuantity( $values['product-id'], $permutation );
|
||||
global $settings;
|
||||
|
||||
$result = [];
|
||||
if ( $quantity )
|
||||
{
|
||||
$msg = $productRepo->getWarehouseMessageNonzero( (int)$values['product-id'], $lang_id );
|
||||
if ( $msg )
|
||||
$result = [ 'msg' => $msg, 'quantity' => $quantity ];
|
||||
else if ( isset( $settings[ 'warehouse_message_nonzero_' . $lang_id ] ) && $settings[ 'warehouse_message_nonzero_' . $lang_id ] )
|
||||
$result = [ 'msg' => $settings[ 'warehouse_message_nonzero_' . $lang_id ], 'quantity' => $quantity ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg = $productRepo->getWarehouseMessageZero( (int)$values['product-id'], $lang_id );
|
||||
if ( $msg )
|
||||
$result = [ 'msg' => $msg, 'quantity' => $quantity ];
|
||||
else if ( isset( $settings[ 'warehouse_message_zero_' . $lang_id ] ) && $settings[ 'warehouse_message_zero_' . $lang_id ] )
|
||||
$result = [ 'msg' => $settings[ 'warehouse_message_zero_' . $lang_id ], 'quantity' => $quantity ];
|
||||
}
|
||||
|
||||
echo json_encode( $result );
|
||||
exit;
|
||||
}
|
||||
@@ -68,10 +91,26 @@ class ShopProductController
|
||||
}
|
||||
|
||||
$product_id = \Shared\Helpers\Helpers::get( 'product_id' );
|
||||
$product = \shop\Product::getFromCache( $product_id, $lang_id );
|
||||
$product_data = $product->getProductDataBySelectedAttributes( $combination );
|
||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||
$product = $productRepo->findCached( $product_id, $lang_id );
|
||||
$product_data = $productRepo->getProductDataBySelectedAttributes( $product, $combination );
|
||||
|
||||
echo json_encode( [ 'product_data' => $product_data ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
private static function getPermutation( $attributes )
|
||||
{
|
||||
if ( !is_array( $attributes ) || !count( $attributes ) ) return null;
|
||||
return implode( '|', $attributes );
|
||||
}
|
||||
|
||||
private static function getPermutationQuantity( $productId, $permutation )
|
||||
{
|
||||
global $mdb;
|
||||
if ( !$permutation ) return $mdb->get( 'pp_shop_products', 'quantity', [ 'id' => $productId ] );
|
||||
$qty = $mdb->get( 'pp_shop_products', 'quantity', [ 'AND' => [ 'parent_id' => $productId, 'permutation_hash' => $permutation ] ] );
|
||||
if ( $qty !== null ) return $qty;
|
||||
return $mdb->get( 'pp_shop_products', 'quantity', [ 'id' => $productId ] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
namespace front;
|
||||
use shop\Product;
|
||||
|
||||
class LayoutEngine
|
||||
{
|
||||
@@ -194,7 +193,7 @@ class LayoutEngine
|
||||
//
|
||||
if ( \Shared\Helpers\Helpers::get( 'product' ) )
|
||||
{
|
||||
$product = Product::getFromCache( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $_GET['permutation_hash'] );
|
||||
$product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $_GET['permutation_hash'] );
|
||||
|
||||
if ( $product['language']['meta_title'] )
|
||||
$page['language']['title'] = $product['language']['meta_title'];
|
||||
@@ -243,7 +242,7 @@ class LayoutEngine
|
||||
$html = str_replace(
|
||||
'[PRODUKT:' . $single_product[1] . ']',
|
||||
\Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( (int)$single_product[1], $lang_id )
|
||||
'product' => ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( (int)$single_product[1], $lang_id )
|
||||
] ),
|
||||
$html
|
||||
);
|
||||
@@ -259,7 +258,7 @@ class LayoutEngine
|
||||
$products_id = explode( ',', $products_box[1] );
|
||||
|
||||
foreach ( $products_id as $product_id )
|
||||
$products[] = Product::getFromCache( (int)$product_id, $lang_id );
|
||||
$products[] = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( (int)$product_id, $lang_id );
|
||||
|
||||
|
||||
$html = str_replace(
|
||||
@@ -288,7 +287,7 @@ class LayoutEngine
|
||||
$products_id_arr = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->topProductIds( $limit );
|
||||
|
||||
foreach ( $products_id_arr as $product_id ){
|
||||
$top_products_arr[] = Product::getFromCache( (int)$product_id, $lang_id );
|
||||
$top_products_arr[] = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( (int)$product_id, $lang_id );
|
||||
}
|
||||
|
||||
$html = str_replace( $pattern,
|
||||
@@ -317,7 +316,7 @@ class LayoutEngine
|
||||
|
||||
|
||||
foreach ( $products_id_arr as $product_id ){
|
||||
$top_products_arr[] = Product::getFromCache( (int)$product_id, $lang_id );
|
||||
$top_products_arr[] = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( (int)$product_id, $lang_id );
|
||||
}
|
||||
|
||||
$html = str_replace( $pattern,
|
||||
@@ -334,7 +333,7 @@ class LayoutEngine
|
||||
$html = str_replace( '[META_DESCRIPTION]', $page['language']['meta_description'], $html );
|
||||
$html = str_replace( '[JEZYKI]', \front\Views\Languages::render( ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->activeLanguages() ), $html );
|
||||
$html = str_replace( '[TYTUL_STRONY]', self::title( $page['language']['title'], $page['show_title'], $page['language']['page_title'] ), $html );
|
||||
$html = str_replace( '[WYSZUKIWARKA]', \shop\Search::simple_form(), $html );
|
||||
$html = str_replace( '[WYSZUKIWARKA]', \front\Views\ShopSearch::simpleForm(), $html );
|
||||
|
||||
/* atrybut noindex */
|
||||
if ( \Shared\Helpers\Helpers::get( 'article' ) )
|
||||
|
||||
10
autoload/front/Views/ShopSearch.php
Normal file
10
autoload/front/Views/ShopSearch.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace front\Views;
|
||||
|
||||
class ShopSearch
|
||||
{
|
||||
public static function simpleForm()
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'shop-search/simple-form' );
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class Basket implements \ArrayAccess
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static public function validate_basket( $basket )
|
||||
{
|
||||
if ( !is_array( $basket ) )
|
||||
return $basket = array();
|
||||
else
|
||||
return $basket;
|
||||
}
|
||||
|
||||
// sprawdzanie czy ilość produktu w koszyku nie jest większa niż stan magazynowy
|
||||
public static function check_product_quantity_in_stock( $basket, bool $message = false )
|
||||
{
|
||||
$result = false;
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$permutation = null;
|
||||
|
||||
if ( isset( $val['parent_id'] ) and (int)$val['parent_id'] and isset( $val['product-id'] ) )
|
||||
$permutation = \shop\Product::get_product_permutation_hash( (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 = \shop\Product::get_product_permutation_quantity_options(
|
||||
$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;
|
||||
}
|
||||
|
||||
public function __get( $variable )
|
||||
{
|
||||
if ( array_key_exists( $variable, $this -> data ) )
|
||||
return $this -> $variable;
|
||||
}
|
||||
|
||||
public function __set( $variable, $value )
|
||||
{
|
||||
$this -> $variable = $value;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class Category implements \ArrayAccess
|
||||
{
|
||||
public function __construct( int $category_id, $lang_id = null )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static public function get_category_products_id( int $category_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_shop_products_categories', 'product_id', [ 'category_id' => $category_id ] );
|
||||
}
|
||||
|
||||
static public function get_category_name( int $category_id, $lang_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$lang_id )
|
||||
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||
|
||||
return $mdb -> get( 'pp_shop_categories_langs', 'title', [ 'AND' => [ 'category_id' => $category_id, 'lang_id' => $lang_id ] ] );
|
||||
}
|
||||
|
||||
public function __get( $variable )
|
||||
{
|
||||
if ( array_key_exists( $variable, $this -> data ) )
|
||||
return $this -> $variable;
|
||||
}
|
||||
|
||||
public function __set( $variable, $value )
|
||||
{
|
||||
$this -> $variable = $value;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
|
||||
static public function get_subcategory_by_category( int $category_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\Category::get_subcategory_by_category:$category_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$shop_categories = $mdb -> select( 'pp_shop_categories', '*', ['parent_id' => $category_id] );
|
||||
$shop_categories_langs = array();
|
||||
foreach ($shop_categories as $shop_categorie) {
|
||||
array_push($shop_categories_langs, $mdb -> get( 'pp_shop_categories_langs', '*', ['category_id' => $shop_categorie['id']] ));
|
||||
}
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $shop_categories_langs );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $shop_categories_langs;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
class Coupon implements \ArrayAccess
|
||||
{
|
||||
private $data = [];
|
||||
|
||||
public function __construct( int $element_id )
|
||||
{
|
||||
global $mdb;
|
||||
if ( $element_id )
|
||||
{
|
||||
$result = $mdb -> get( 'pp_shop_coupon', '*', [ 'id' => $element_id ] );
|
||||
if ( is_array( $result ) )
|
||||
$this -> data = $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function load_from_db_by_name( string $name )
|
||||
{
|
||||
global $mdb;
|
||||
$result = $mdb -> get( 'pp_shop_coupon', '*', [ 'name' => $name ] );
|
||||
if ( is_array( $result ) )
|
||||
$this -> data = $result;
|
||||
}
|
||||
|
||||
public function is_one_time()
|
||||
{
|
||||
return (bool)( $this -> data['one_time'] ?? 0 );
|
||||
}
|
||||
|
||||
public function is_available()
|
||||
{
|
||||
if ( !( $this -> data['id'] ?? 0 ) )
|
||||
return false;
|
||||
|
||||
if ( !( $this -> data['status'] ?? 0 ) )
|
||||
return false;
|
||||
|
||||
return !( $this -> data['used'] ?? 0 );
|
||||
}
|
||||
|
||||
public function set_as_used()
|
||||
{
|
||||
$id = (int)( $this -> data['id'] ?? 0 );
|
||||
if ( $id > 0 )
|
||||
{
|
||||
global $mdb;
|
||||
$repo = new \Domain\Coupon\CouponRepository( $mdb );
|
||||
$repo -> markAsUsed( $id );
|
||||
$this -> data['used'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function __get( $variable )
|
||||
{
|
||||
return isset( $this -> data[$variable] ) ? $this -> data[$variable] : null;
|
||||
}
|
||||
|
||||
public function __set( $variable, $value )
|
||||
{
|
||||
$this -> data[$variable] = $value;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> data[$offset] );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return isset( $this -> data[$offset] ) ? $this -> data[$offset] : null;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> data[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> data[$offset] );
|
||||
}
|
||||
}
|
||||
@@ -1,562 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class Order implements \ArrayAccess
|
||||
{
|
||||
private const APILO_SYNC_QUEUE_FILE = '/temp/apilo-sync-queue.json';
|
||||
|
||||
public $id;
|
||||
public $products;
|
||||
public $statuses;
|
||||
public $status;
|
||||
public $client_email;
|
||||
public $summary;
|
||||
public $apilo_order_id;
|
||||
public $date_order;
|
||||
public $payment_method_id;
|
||||
public $transport_cost;
|
||||
public $number;
|
||||
|
||||
public function __construct( int $order_id, $hash = '', $przelewy24_hash = '' )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $order_id )
|
||||
{
|
||||
$result = $mdb -> get( 'pp_shop_orders', '*', [ 'id' => $order_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
}
|
||||
|
||||
if ( $hash )
|
||||
{
|
||||
$result = $mdb -> get( 'pp_shop_orders', '*', [ 'hash' => $hash ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
}
|
||||
|
||||
if ( $przelewy24_hash )
|
||||
{
|
||||
$result = $mdb -> get( 'pp_shop_orders', '*', [ 'przelewy24_hash' => $przelewy24_hash ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
}
|
||||
|
||||
$this -> products = $mdb -> select( 'pp_shop_order_products', '*', [ 'order_id' => $order_id ] );
|
||||
$this -> statuses = $mdb -> select( 'pp_shop_order_statuses', '*', [ 'order_id' => $order_id, 'ORDER' => [ 'id' => 'DESC' ] ] );
|
||||
}
|
||||
|
||||
static public function notes_save( int $order_id, $notes )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_shop_orders', [ 'notes' => $notes ], [ 'id' => $order_id ] );
|
||||
}
|
||||
|
||||
public static function order_statuses()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$repository = new \Domain\ShopStatus\ShopStatusRepository( $mdb );
|
||||
return $repository -> allStatuses();
|
||||
}
|
||||
|
||||
public function update_aplio_order_status_date( $date )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_shop_orders', [ 'apilo_order_status_date' => $date ], [ 'id' => $this -> id ] );
|
||||
}
|
||||
|
||||
// set_as_unpaid
|
||||
public function set_as_unpaid() {
|
||||
global $mdb;
|
||||
|
||||
$mdb -> update( 'pp_shop_orders', [ 'paid' => 0 ], [ 'id' => $this -> id ] );
|
||||
return true;
|
||||
}
|
||||
|
||||
// set_as_paid
|
||||
public function set_as_paid( $send_email = false )
|
||||
{
|
||||
global $mdb, $config;
|
||||
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb );
|
||||
|
||||
// apilo
|
||||
$apilo_settings = $integrationsRepository -> getSettings( 'apilo' );
|
||||
if ( $apilo_settings['enabled'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders'] )
|
||||
{
|
||||
// put data to file
|
||||
if ( $config['debug']['apilo'] )
|
||||
{
|
||||
file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', date( 'Y-m-d H:i:s' ) . " --- SET AS PAID\n\n", FILE_APPEND );
|
||||
file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $this, true ) . "\n\n", FILE_APPEND );
|
||||
}
|
||||
|
||||
if ( $this -> apilo_order_id and !$this -> sync_apilo_payment() )
|
||||
{
|
||||
self::queue_apilo_sync( (int)$this -> id, true, null, 'payment_sync_failed' );
|
||||
}
|
||||
}
|
||||
|
||||
$mdb -> update( 'pp_shop_orders', [ 'paid' => 1 ], [ 'id' => $this -> id ] );
|
||||
$this -> update_status( 1, $send_email );
|
||||
|
||||
$dir_logs = $_SERVER['DOCUMENT_ROOT'] . '/logs/';
|
||||
if ( !is_dir( $dir_logs ) )
|
||||
mkdir( $dir_logs, 0777, true );
|
||||
|
||||
// $file = $dir_logs . date( 'Y-m-d' ) . '-order-set-as-paid.txt';
|
||||
// $content = date( 'Y-m-d H:i:s' ) . ' | ' . $this -> id . ' | ' . $this -> number . ' | ' . $this -> summary . ' | ' . $this -> client_email . "\n";
|
||||
// $content .= print_r( $apilo_response, true ) . "\n\n";
|
||||
// file_put_contents( $file, $content, FILE_APPEND );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// zmiana statusu zamówienia
|
||||
public function update_status( int $status, int $email_send )
|
||||
{
|
||||
global $mdb, $config;
|
||||
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb );
|
||||
|
||||
if ( $this -> status == $status )
|
||||
return false;
|
||||
|
||||
if ( $mdb -> update( 'pp_shop_orders', [ 'status' => $status ], [ 'id' => $this -> id ] ) )
|
||||
{
|
||||
$mdb -> insert( 'pp_shop_order_statuses', [
|
||||
'order_id' => $this -> id,
|
||||
'status_id' => $status,
|
||||
'mail' => $email_send
|
||||
] );
|
||||
|
||||
if ( $email_send )
|
||||
{
|
||||
$this -> status = $status;
|
||||
$response['email'] = $this -> send_status_change_email();
|
||||
}
|
||||
|
||||
$response['result'] = true;
|
||||
|
||||
// apilo
|
||||
$apilo_settings = $integrationsRepository -> getSettings( 'apilo' );
|
||||
if ( $apilo_settings['enabled'] and $apilo_settings['access-token'] and $apilo_settings['sync_orders'] )
|
||||
{
|
||||
// put data to file
|
||||
if ( $config['debug']['apilo'] )
|
||||
{
|
||||
file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', date( 'Y-m-d H:i:s' ) . " --- UPDATE STATUS\n\n", FILE_APPEND );
|
||||
file_put_contents( $_SERVER['DOCUMENT_ROOT'] . '/logs/apilo.txt', print_r( $this, true ) . "\n\n", FILE_APPEND );
|
||||
}
|
||||
|
||||
if ( $this -> apilo_order_id and !$this -> sync_apilo_status( (int)$status ) )
|
||||
{
|
||||
self::queue_apilo_sync( (int)$this -> id, false, (int)$status, 'status_sync_failed' );
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ponowne wysłanie maila o złożonym zamówieniu
|
||||
public function order_resend_confirmation_email()
|
||||
{
|
||||
global $settings;
|
||||
|
||||
global $mdb;
|
||||
$order = ( new \Domain\Order\OrderRepository( $mdb ) )->orderDetailsFrontend( $this -> id );
|
||||
$coupon = (int)$order['coupon_id'] ? new \shop\Coupon( (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( $this -> 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;
|
||||
}
|
||||
|
||||
// wysłanie maila o zmianie statusu
|
||||
public function send_status_change_email()
|
||||
{
|
||||
if ( !$this -> client_email )
|
||||
return false;
|
||||
|
||||
$order_statuses = self::order_statuses();
|
||||
|
||||
$firm_name = ( new \Domain\Settings\SettingsRepository( $mdb ) )->getSingleValue( 'firm_name' );
|
||||
|
||||
switch( $this -> status ):
|
||||
case 0:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało złożone' );
|
||||
break;
|
||||
case 1:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało opłacone' );
|
||||
break;
|
||||
case 2:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - płatność za zamówienie [NUMER] została odrzucona' );
|
||||
break;
|
||||
case 3:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - płatność za zamówienie [NUMER] jest sprawdzania ręcznie' );
|
||||
break;
|
||||
case 4:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało przyjęte do realizacji' );
|
||||
break;
|
||||
case 5:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało wysłane' );
|
||||
break;
|
||||
case 6:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało zrealizowane' );
|
||||
break;
|
||||
case 7:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało przygotowane go wysłania' );
|
||||
break;
|
||||
case 8:
|
||||
$subject = str_replace( '[NUMER]', $this -> number, $firm_name . ' - zamówienie [NUMER] zostało anulowane' );
|
||||
break;
|
||||
endswitch;
|
||||
|
||||
$email = new \Email( 0 );
|
||||
$email -> load_by_name( '#sklep-zmiana-statusu-zamowienia' );
|
||||
|
||||
$email -> text = str_replace( '[NUMER_ZAMOWIENIA]', $this -> number, $email -> text );
|
||||
$email -> text = str_replace( '[DATA_ZAMOWIENIA]', date( 'Y/m/d', strtotime( $this -> date_order ) ), $email -> text );
|
||||
$email -> text = str_replace( '[STATUS]', $order_statuses[ $this -> status ], $email -> text );
|
||||
|
||||
return $email -> send( $this -> client_email, $subject, true );
|
||||
}
|
||||
|
||||
// wyliczanie wartości zamówienia podczas edycji zamówienia
|
||||
static public function calculate_order_summary_by_admin( int $order_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$rows = $mdb -> select( 'pp_shop_order_products', '*', [ 'order_id' => $order_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $rows ) ) foreach ( $rows as $row )
|
||||
{
|
||||
if ( $row['price_brutto_promo'] )
|
||||
$summary += $row['price_brutto_promo'] * $row['quantity'];
|
||||
else
|
||||
$summary += $row['price_brutto'] * $row['quantity'];
|
||||
}
|
||||
|
||||
return $summary + $mdb -> get( 'pp_shop_orders', 'transport_cost', [ 'id' => $order_id ] );
|
||||
}
|
||||
|
||||
// ADMIN - usunięcie zamówienia
|
||||
static public function order_delete( int $order_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> delete( 'pp_shop_orders', [ 'id' => $order_id ] );
|
||||
}
|
||||
|
||||
// ADMIN - zmiana zamówienia
|
||||
static public function order_save_by_admin( int $order_id, string $client_name, string $client_surname, string $client_street, string $client_postal_code, string $client_city, string $client_email, $firm_name = '', $firm_street = '', $firm_postal_code = '', $firm_city = '', $firm_nip = '',
|
||||
int $transport_id, string $inpost_paczkomat, int $payment_method_id )
|
||||
{
|
||||
global $mdb, $user;
|
||||
|
||||
$mdb -> update( 'pp_shop_orders', [
|
||||
'client_name' => $client_name,
|
||||
'client_surname' => $client_surname,
|
||||
'client_street' => $client_street,
|
||||
'client_postal_code' => $client_postal_code,
|
||||
'client_city' => $client_city,
|
||||
'client_email' => $client_email,
|
||||
'firm_name' => $firm_name ? $firm_name : null,
|
||||
'firm_street' => $firm_street ? $firm_street : null,
|
||||
'firm_postal_code' => $firm_postal_code ? $firm_postal_code : null,
|
||||
'firm_city' => $firm_city ? $firm_city : null,
|
||||
'firm_nip' => $firm_nip ? $firm_nip : null,
|
||||
'transport_id' => $transport_id,
|
||||
'transport' => $mdb -> get( 'pp_shop_transports', 'name_visible', [ 'id' => $transport_id ] ),
|
||||
'transport_cost' => $mdb -> get( 'pp_shop_transports', 'cost', [ 'id' => $transport_id ] ),
|
||||
'transport_description' => $mdb -> get( 'pp_shop_transports', 'description', [ 'id' => $transport_id ] ),
|
||||
'inpost_paczkomat' => $inpost_paczkomat,
|
||||
'payment_method_id' => $payment_method_id,
|
||||
'payment_method' => $mdb -> get( 'pp_shop_payment_methods', 'name', [ 'id' => $payment_method_id ] ),
|
||||
], [
|
||||
'id' => $order_id
|
||||
] );
|
||||
|
||||
$mdb -> update( 'pp_shop_orders', [
|
||||
'summary' => \shop\Order::calculate_order_summary_by_admin( $order_id )
|
||||
], [
|
||||
'id' => $order_id
|
||||
] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
|
||||
private function sync_apilo_payment(): bool
|
||||
{
|
||||
global $config, $mdb;
|
||||
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb );
|
||||
|
||||
if ( !(int)$this -> apilo_order_id )
|
||||
return true;
|
||||
|
||||
$payment_type = (int)( new \Domain\PaymentMethod\PaymentMethodRepository( $mdb ) )->getApiloPaymentTypeId( (int)$this -> payment_method_id );
|
||||
if ( $payment_type <= 0 )
|
||||
$payment_type = 1;
|
||||
|
||||
$payment_date = new \DateTime( $this -> date_order );
|
||||
$access_token = $integrationsRepository -> apiloGetAccessToken();
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt( $ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $this -> apilo_order_id . '/payment/' );
|
||||
curl_setopt( $ch, CURLOPT_POST, 1 );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [
|
||||
'amount' => str_replace( ',', '.', $this -> 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 ( $config['debug']['apilo'] )
|
||||
{
|
||||
self::append_apilo_log( "PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r( $apilo_response, true ) . "\n" );
|
||||
}
|
||||
|
||||
if ( $curl_error !== '' )
|
||||
return false;
|
||||
|
||||
if ( $http_code < 200 or $http_code >= 300 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function sync_apilo_status( int $status ): bool
|
||||
{
|
||||
global $config, $mdb;
|
||||
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb );
|
||||
|
||||
if ( !(int)$this -> apilo_order_id )
|
||||
return true;
|
||||
|
||||
$access_token = $integrationsRepository -> apiloGetAccessToken();
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt( $ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $this -> apilo_order_id . '/status/' );
|
||||
curl_setopt( $ch, CURLOPT_POST, 1 );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [
|
||||
'id' => $this -> apilo_order_id,
|
||||
'status' => (int)( new \Domain\ShopStatus\ShopStatusRepository( $mdb ) )->getApiloStatusId( (int)$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 ( $config['debug']['apilo'] )
|
||||
{
|
||||
self::append_apilo_log( "STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r( $apilo_result, true ) . "\n" );
|
||||
}
|
||||
|
||||
if ( $curl_error !== '' )
|
||||
return false;
|
||||
|
||||
if ( $http_code < 200 or $http_code >= 300 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function process_apilo_sync_queue( int $limit = 10 ): int
|
||||
{
|
||||
$queue = self::load_apilo_sync_queue();
|
||||
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 = new self( $order_id );
|
||||
if ( !(int)$order -> id )
|
||||
{
|
||||
unset( $queue[$key] );
|
||||
continue;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$sync_failed = false;
|
||||
|
||||
$payment_pending = !empty( $task['payment'] ) and (int)$order -> paid === 1;
|
||||
if ( $payment_pending and (int)$order -> apilo_order_id )
|
||||
{
|
||||
if ( !$order -> sync_apilo_payment() )
|
||||
{
|
||||
$sync_failed = true;
|
||||
$error = 'payment_sync_failed';
|
||||
}
|
||||
}
|
||||
|
||||
$status_pending = isset( $task['status'] ) and $task['status'] !== null and $task['status'] !== '';
|
||||
if ( !$sync_failed and $status_pending and (int)$order -> apilo_order_id )
|
||||
{
|
||||
if ( !$order -> sync_apilo_status( (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::save_apilo_sync_queue( $queue );
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
private static function queue_apilo_sync( int $order_id, bool $payment, ?int $status, string $error ): void
|
||||
{
|
||||
if ( $order_id <= 0 )
|
||||
return;
|
||||
|
||||
$queue = self::load_apilo_sync_queue();
|
||||
$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::save_apilo_sync_queue( $queue );
|
||||
}
|
||||
|
||||
private static function apilo_sync_queue_path(): string
|
||||
{
|
||||
return dirname( __DIR__, 2 ) . self::APILO_SYNC_QUEUE_FILE;
|
||||
}
|
||||
|
||||
private static function load_apilo_sync_queue(): array
|
||||
{
|
||||
$path = self::apilo_sync_queue_path();
|
||||
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 save_apilo_sync_queue( array $queue ): void
|
||||
{
|
||||
$path = self::apilo_sync_queue_path();
|
||||
$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 append_apilo_log( 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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,952 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
use shop\Shop;
|
||||
use S;
|
||||
class Product implements \ArrayAccess
|
||||
{
|
||||
public function __construct( int $product_id, $lang_id = null, $permutation_hash = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$lang_id )
|
||||
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||
|
||||
$result = $mdb -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
|
||||
// kombinacja produktu
|
||||
if ( $this -> parent_id )
|
||||
{
|
||||
// pobranie wartości z produktu głównego
|
||||
if ( !$this -> price_netto or !$this -> price_brutto )
|
||||
{
|
||||
$result = $mdb -> get( 'pp_shop_products', [ 'price_netto', 'price_brutto', 'price_netto_promo', 'price_brutto_promo', 'vat', 'wp' ], [ 'id' => $this -> parent_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
}
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $this -> parent_id, 'lang_id' => $lang_id ] ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $row[ 'copy_from' ] )
|
||||
{
|
||||
$results2 = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $this -> parent_id, 'lang_id' => $row[ 'copy_from' ] ] ] );
|
||||
if ( is_array( $results2 ) )
|
||||
foreach ( $results2 as $row2 )
|
||||
$this -> language = $row2;
|
||||
}
|
||||
else
|
||||
$this -> language = $row;
|
||||
}
|
||||
|
||||
$this -> images = $mdb -> select( 'pp_shop_products_images', '*', [ 'product_id' => $this -> parent_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$this -> files = $mdb -> select( 'pp_shop_products_files', '*', [ 'product_id' => $this -> parent_id] );
|
||||
$this -> categories = $mdb -> select( 'pp_shop_products_categories', 'category_id', [ 'product_id' => $this -> parent_id ] );
|
||||
|
||||
$this -> products_related = $mdb -> select( 'pp_shop_products_related', 'product_related_id', [ 'product_id' => $this -> parent_id ] );
|
||||
$this -> products_sets = $mdb -> select( 'pp_shop_product_sets_products', 'product_id', [ 'AND' => [ 'set_id' => $this -> set_id, 'product_id[!]' => $this -> parent_id ] ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lang_id ] ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $row[ 'copy_from' ] )
|
||||
{
|
||||
$results2 = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $row[ 'copy_from' ] ] ] );
|
||||
if ( is_array( $results2 ) )
|
||||
foreach ( $results2 as $row2 )
|
||||
$this -> language = $row2;
|
||||
}
|
||||
else
|
||||
$this -> language = $row;
|
||||
}
|
||||
|
||||
$this -> images = $mdb -> select( 'pp_shop_products_images', '*', [ 'product_id' => $product_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'ASC' ] ] );
|
||||
$this -> files = $mdb -> select( 'pp_shop_products_files', '*', [ 'product_id' => $product_id ] );
|
||||
$this -> categories = $mdb -> select( 'pp_shop_products_categories', 'category_id', [ 'product_id' => $product_id ] );
|
||||
|
||||
$this -> products_related = $mdb -> select( 'pp_shop_products_related', 'product_related_id', [ 'product_id' => $product_id ] );
|
||||
$this -> products_sets = $mdb -> select( 'pp_shop_product_sets_products', 'product_id', [ 'AND' => [ 'set_id' => $this -> set_id, 'product_id[!]' => $product_id ] ] );
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_products', 'id', [ 'parent_id' => $product_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
$product_combinations[] = \shop\Product::getFromCache( $row, $lang_id );
|
||||
|
||||
$this -> product_combinations = $product_combinations;
|
||||
}
|
||||
}
|
||||
|
||||
$producer = $mdb -> get( 'pp_shop_producer', '*', [ 'id' => (int) $this -> producer_id ] );
|
||||
$producer_languages = $mdb -> get( 'pp_shop_producer_lang', '*', [ 'AND' => [ 'producer_id' => (int) $this -> producer_id, 'lang_id' => $lang_id ] ] );
|
||||
$producer['description'] = $producer_languages['description'];
|
||||
$producer['data'] = $producer_languages['data'];
|
||||
$producer['meta_title'] = $producer_languages['meta_title'];
|
||||
|
||||
$this -> producer = $producer;
|
||||
|
||||
if ( $permutation_hash )
|
||||
{
|
||||
$permutation_price = $mdb -> get( 'pp_shop_products', [ 'price_netto', 'price_brutto', 'price_netto_promo', 'price_brutto_promo' ], [ 'AND' => [ 'permutation_hash' => $permutation_hash, 'parent_id' => $product_id ] ] );
|
||||
if ( is_array( $permutation_price ) )
|
||||
{
|
||||
if ( $permutation_price[ 'price_netto' ] != null )
|
||||
$this -> price_netto = $permutation_price[ 'price_netto' ];
|
||||
|
||||
if ( $permutation_price[ 'price_brutto' ] != null )
|
||||
$this -> price_brutto = $permutation_price[ 'price_brutto' ];
|
||||
|
||||
if ( $permutation_price[ 'price_netto_promo' ] != null )
|
||||
$this -> price_netto_promo = $permutation_price[ 'price_netto_promo' ];
|
||||
|
||||
if ( $permutation_price[ 'price_brutto_promo' ] != null )
|
||||
$this -> price_brutto_promo = $permutation_price[ 'price_brutto_promo' ];
|
||||
}
|
||||
}
|
||||
|
||||
$this -> custom_fields = $mdb -> select( 'pp_shop_products_custom_fields', '*', [ 'id_product' => $product_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product object from cache or creates a new instance if not found.
|
||||
*
|
||||
* @param int $product_id The ID of the product.
|
||||
* @param int $lang_id The ID of the language.
|
||||
* @param string $permutation_hash The permutation hash of the product.
|
||||
* @return \shop\Product The product object.
|
||||
*/
|
||||
public static function getFromCache($product_id, $lang_id, $permutation_hash = null)
|
||||
{
|
||||
// Check if Redis extension is loaded
|
||||
if (class_exists('Redis'))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the Redis connection instance
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
||||
|
||||
// Check if Redis connection is valid
|
||||
if ( $redis )
|
||||
{
|
||||
// Try to retrieve the serialized product object from cache
|
||||
$objectData = $redis->get("shop\product:$product_id:$lang_id:$permutation_hash");
|
||||
|
||||
if (!$objectData)
|
||||
{
|
||||
// Product not found in cache, create a new instance and store it in cache
|
||||
$object = new self($product_id, $lang_id, $permutation_hash);
|
||||
$redis->setex("shop\product:$product_id:$lang_id:$permutation_hash", 60 * 60 * 24, serialize($object));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Product found in cache, unserialize it
|
||||
$object = unserialize($objectData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redis connection failed, create a new instance
|
||||
$object = new self($product_id, $lang_id, $permutation_hash);
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Log the exception if needed
|
||||
$object = new self($product_id, $lang_id, $permutation_hash);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redis extension not loaded, create a new instance
|
||||
$object = new self($product_id, $lang_id, $permutation_hash);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function getDefaultCombinationPrices()
|
||||
{
|
||||
$permutation_hash = '';
|
||||
|
||||
$attributes = \shop\Product::get_product_attributes( $this -> product_combinations );
|
||||
foreach ( $attributes as $attribute )
|
||||
{
|
||||
foreach ( $attribute['values'] as $value )
|
||||
{
|
||||
if ( $value['is_default'] )
|
||||
{
|
||||
if ( $permutation_hash )
|
||||
$permutation_hash .= '|';
|
||||
$permutation_hash .= $attribute['id'] . '-' . $value['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $this -> product_combinations as $product_combination )
|
||||
{
|
||||
if ( $product_combination -> permutation_hash == $permutation_hash )
|
||||
{
|
||||
$prices['price_netto'] = $product_combination -> price_netto;
|
||||
$prices['price_brutto'] = $product_combination -> price_brutto;
|
||||
$prices['price_netto_promo'] = $product_combination -> price_netto_promo;
|
||||
$prices['price_brutto_promo'] = $product_combination -> price_brutto_promo;
|
||||
}
|
||||
}
|
||||
|
||||
return $prices;
|
||||
}
|
||||
|
||||
public function generateSubtitleFromAttributes( $permutation_hash )
|
||||
{
|
||||
global $lang_id;
|
||||
|
||||
$subtitle = '';
|
||||
|
||||
$attributes = explode( '|', $permutation_hash );
|
||||
foreach ( $attributes as $attribute )
|
||||
{
|
||||
$attribute = explode( '-', $attribute );
|
||||
|
||||
if ( $subtitle )
|
||||
$subtitle .= ', ';
|
||||
|
||||
$subtitle .= \shop\ProductAttribute::getAttributeName( $attribute[0], $lang_id ) . ': ' . \shop\ProductAttribute::get_value_name( $attribute[1], $lang_id );
|
||||
}
|
||||
|
||||
return $subtitle;
|
||||
}
|
||||
|
||||
public function getProductDataBySelectedAttributes( $selected_attribute )
|
||||
{
|
||||
global $settings;
|
||||
|
||||
foreach ( $this -> product_combinations as $product_combination )
|
||||
{
|
||||
if ( $product_combination -> permutation_hash == $selected_attribute )
|
||||
{
|
||||
if ( $product_combination -> quantity !== null or $product_combination -> stock_0_buy )
|
||||
{
|
||||
$result['quantity'] = $product_combination -> quantity;
|
||||
$result['stock_0_buy'] = $product_combination -> stock_0_buy;
|
||||
$result['price_netto'] = Shop::shortPrice( $product_combination -> price_netto );
|
||||
$result['price_brutto'] = Shop::shortPrice( $product_combination -> price_brutto );
|
||||
$result['price_netto_promo'] = $product_combination -> price_netto_promo ? Shop::shortPrice( $product_combination -> price_netto_promo ) : null;
|
||||
$result['price_brutto_promo'] = $product_combination -> price_brutto_promo ? Shop::shortPrice( $product_combination -> price_brutto_promo ) : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result['quantity'] = $this -> quantity;
|
||||
$result['stock_0_buy'] = $this -> stock_0_buy;
|
||||
$result['price_netto'] = Shop::shortPrice( $this -> price_netto );
|
||||
$result['price_brutto'] = Shop::shortPrice( $this -> price_brutto );
|
||||
$result['price_netto_promo'] = $this -> price_netto_promo ? Shop::shortPrice( $this -> price_netto_promo ) : null;
|
||||
$result['price_brutto_promo'] = $this -> price_brutto_promo ? Shop::shortPrice( $this -> price_brutto_promo ) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result['messages']['warehouse_message_zero'] = $this -> language['warehouse_message_zero'] ? $this -> language['warehouse_message_zero'] : $settings['warehouse_message_zero_pl'];
|
||||
$result['messages']['warehouse_message_nonzero'] = $this -> language['warehouse_message_nonzero'] ? $this -> language['warehouse_message_nonzero'] : $settings['warehouse_message_nonzero_pl'];
|
||||
$result['permutation_hash'] = $selected_attribute;
|
||||
return $result;
|
||||
}
|
||||
|
||||
// sprawdź czy produkt jest na promocji
|
||||
static public function is_product_on_promotion( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $mdb -> get( 'pp_shop_products', 'price_netto_promo', [ 'id' => $product_id ] ) != null )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// pobierz kod SKU
|
||||
static public function get_product_sku( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> get( 'pp_shop_products', [ 'parent_id', 'sku' ], [ 'id' => $product_id ] );
|
||||
if ( $result['sku'] )
|
||||
return $result['sku'];
|
||||
else
|
||||
return $mdb -> get( 'pp_shop_products', 'sku', [ 'id' => $result['parent_id'] ] );
|
||||
}
|
||||
|
||||
// pobierz cenę produktu
|
||||
// FASADA - wywołuje nową klasę Domain\Product\ProductRepository
|
||||
static public function get_product_price( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
$repository = new \Domain\Product\ProductRepository($mdb);
|
||||
return $repository->getPrice($product_id);
|
||||
}
|
||||
|
||||
// pobierz nazwę produktu
|
||||
// FASADA - wywołuje nową klasę Domain\Product\ProductRepository
|
||||
static public function get_product_name( int $product_id, string $lang_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$lang_id )
|
||||
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||
|
||||
$repository = new \Domain\Product\ProductRepository($mdb);
|
||||
return $repository->getName($product_id, $lang_id);
|
||||
}
|
||||
|
||||
// pobierz i wyświetl produktu do zestawu po dodaniu do koszyka
|
||||
static public function product_sets_when_add_to_basket( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\Product::product_sets_when_add_to_basket:$product_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
if ( $product_id_tmp = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product_id ] ) )
|
||||
$set_id = $mdb -> get( 'pp_shop_products', 'set_id', [ 'id' => $product_id_tmp ] );
|
||||
else
|
||||
$set_id = $mdb -> get( 'pp_shop_products', 'set_id', [ 'id' => $product_id ] );
|
||||
|
||||
$products = $mdb -> select( 'pp_shop_product_sets_products', 'product_id', [ 'set_id' => $set_id ] );
|
||||
if ( !$products )
|
||||
{
|
||||
$products_intersection = $mdb -> select( 'pp_shop_orders_products_intersection', [ 'product_1_id', 'product_2_id' ], [ 'OR' => [ 'product_1_id' => $product_id, 'product_2_id' => $product_id ], 'ORDER' => [ 'count' => 'DESC' ], 'LIMIT' => 5 ] );
|
||||
if ( !count( $products_intersection ) )
|
||||
{
|
||||
$product_id = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product_id ] );
|
||||
$products_intersection = $mdb -> select( 'pp_shop_orders_products_intersection', [ 'product_1_id', 'product_2_id' ], [ 'OR' => [ 'product_1_id' => $product_id, 'product_2_id' => $product_id ], 'ORDER' => [ 'count' => 'DESC' ], 'LIMIT' => 5 ] );
|
||||
}
|
||||
|
||||
foreach ( $products_intersection as $product_intersection )
|
||||
{
|
||||
if ( $product_intersection['product_1_id'] != $product_id )
|
||||
$products[] = $product_intersection['product_1_id'];
|
||||
else
|
||||
$products[] = $product_intersection['product_2_id'];
|
||||
}
|
||||
array_unique( $products );
|
||||
}
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $products );
|
||||
}
|
||||
else
|
||||
{
|
||||
$products = unserialize( $objectData );
|
||||
}
|
||||
|
||||
foreach ( $products as $product_id )
|
||||
{
|
||||
if ( !( new \Domain\Product\ProductRepository( $mdb ) )->isProductActiveCached( (int)$product_id ) )
|
||||
$products = array_diff( $products, [ $product_id ] );
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view( 'shop-basket/alert-product-sets', [
|
||||
'products' => $products
|
||||
] );
|
||||
}
|
||||
|
||||
// dodaje 1 do licznika odwiedziń produktu
|
||||
static public function add_visit( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_shop_products', [ 'visits[+]' => 1 ], [ 'id' => $product_id ] );
|
||||
}
|
||||
|
||||
//FIX:ME - do poprawy nazwa
|
||||
// pobierz zdjęcie główne
|
||||
static public function getProductImg( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_products_images', 'src', [ 'product_id' => $product_id, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
return $row;
|
||||
}
|
||||
|
||||
//FIX:ME - do poprawy nazwa
|
||||
// pobierz bezpośredni url produktu
|
||||
static public function getProductUrl( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lang_id ] ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
{
|
||||
if ( $row[ 'copy_from' ] )
|
||||
{
|
||||
$results2 = $mdb -> select( 'pp_shop_products_langs', '*', [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $row[ 'copy_from' ] ] ] );
|
||||
if ( is_array( $results2 ) )
|
||||
foreach ( $results2 as $row2 )
|
||||
$language = $row2;
|
||||
}
|
||||
else
|
||||
$language = $row;
|
||||
}
|
||||
|
||||
$language['seo_link'] ? $url = '/' . $language['seo_link'] : $url = '/p-' . $product_id . '-' . \Shared\Helpers\Helpers::seo( $language['name'] );
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
// pobierz ilość wyników na podstawie wyszukiwanej nazwy
|
||||
static public function searchProductsByNameCount( $query, $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> 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' => $lang_id
|
||||
] ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||
return $results[0]['c'];
|
||||
}
|
||||
|
||||
// pobierz id produktów na podstawie wyszukiwanej nazwy
|
||||
static public function getProductsIdByName( string $query, string $lang_id, int $products_limit, int $from )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> 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 '
|
||||
. $from . ',' . $products_limit, [
|
||||
':query' => '%' . $query . '%',
|
||||
':lang_id' => $lang_id
|
||||
] ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||
if ( is_array( $results ) and !empty( $results ) ) foreach ( $results as $row )
|
||||
$output[] = $row['id'];
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
static public function searchProductsByName( string $query, string $lang_id, int $bs = 0 )
|
||||
{
|
||||
$count = \shop\Product::searchProductsByNameCount( $query, $lang_id );
|
||||
$ls = ceil( $count / 12 );
|
||||
|
||||
if ( $bs < 1 )
|
||||
$bs = 1;
|
||||
else if ( $bs > $ls )
|
||||
$bs = $ls;
|
||||
|
||||
$from = 12 * ( $bs - 1 );
|
||||
|
||||
if ( $from < 0 )
|
||||
$from = 0;
|
||||
|
||||
$results['products'] = \shop\Product::getProductsIdByName( $query, $lang_id, 12, $from );
|
||||
$results['count'] = $count;
|
||||
$results['ls'] = $ls;
|
||||
|
||||
return $results;
|
||||
|
||||
}
|
||||
|
||||
static public function searchProductByNameAjax( string $query, string $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> 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' => $lang_id
|
||||
] ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||
}
|
||||
|
||||
public function permutations()
|
||||
{
|
||||
if ( is_array( $this -> attributes ) ) foreach ( $this -> attributes as $attribute )
|
||||
{
|
||||
$attribute_obj = new \shop\ProductAttribute( $attribute['attribute_id'] );
|
||||
|
||||
if ( $attribute_obj['stock_influences'] )
|
||||
$attributes_tmp[ $attribute[ 'attribute_id' ] ][] = $attribute[ 'value_id' ];
|
||||
}
|
||||
|
||||
if ( is_array( $attributes_tmp ) )
|
||||
return $this -> permutations = self::array_cartesian( $attributes_tmp );
|
||||
}
|
||||
|
||||
/// sprawdź czy produkt można zamawiać przy stanie magazynowym zero
|
||||
static public function is_stock_0_buy( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
// jeżeli jest to kombinacja produktu
|
||||
if ( $parent_id = $mdb -> get( 'pp_shop_products', 'parent_id', [ 'id' => $product_id ] ) )
|
||||
return $mdb -> get( 'pp_shop_products', 'stock_0_buy', [ 'id' => $parent_id ] );
|
||||
else
|
||||
return $mdb -> get( 'pp_shop_products', 'stock_0_buy', [ 'id' => $product_id ] );
|
||||
}
|
||||
|
||||
// pobierz stan magazynowy i komunikaty dla kombinacji produktu
|
||||
static public function get_product_permutation_quantity_options( int $product_id, $permutation )
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\Product::get_product_permutation_quantity_options:v2:$product_id:$permutation";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
if ( $mdb -> count( 'pp_shop_products', [ 'AND' => [ 'parent_id' => $product_id, 'permutation_hash' => $permutation ] ] ) )
|
||||
{
|
||||
$result['quantity'] = $mdb -> get( 'pp_shop_products', 'quantity', [ 'AND' => [ 'parent_id' => $product_id, 'permutation_hash' => $permutation ] ] );
|
||||
$result['stock_0_buy'] = $mdb -> get( 'pp_shop_products', 'stock_0_buy', [ 'AND' => [ 'parent_id' => $product_id, 'permutation_hash' => $permutation ] ] );
|
||||
|
||||
if ( $result['quantity'] == null )
|
||||
{
|
||||
$result['quantity'] = $mdb -> get( 'pp_shop_products', 'quantity', [ 'id' => $product_id ] );
|
||||
|
||||
if ( $result['stock_0_buy'] == null )
|
||||
$result['stock_0_buy'] = $mdb -> get( 'pp_shop_products', 'stock_0_buy', [ 'id' => $product_id] );
|
||||
|
||||
$result['messages'] = $mdb -> get( 'pp_shop_products_langs', [ 'warehouse_message_zero', 'warehouse_message_nonzero' ], [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => 'pl' ] ] );
|
||||
|
||||
if ( !$result['messages']['warehouse_message_zero'] )
|
||||
$result['messages']['warehouse_message_zero'] = $settings['warehouse_message_zero_pl'];
|
||||
|
||||
if ( !$result['messages']['warehouse_message_nonzero'] )
|
||||
$result['messages']['warehouse_message_nonzero'] = $settings['warehouse_message_nonzero_pl'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$result['messages'] = $mdb -> get( 'pp_shop_products_langs', [ 'warehouse_message_zero', 'warehouse_message_nonzero' ], [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => 'pl' ] ] );
|
||||
|
||||
if ( !$result['messages']['warehouse_message_zero'] )
|
||||
$result['messages']['warehouse_message_zero'] = $settings['warehouse_message_zero_pl'];
|
||||
|
||||
if ( !$result['messages']['warehouse_message_nonzero'] )
|
||||
$result['messages']['warehouse_message_nonzero'] = $settings['warehouse_message_nonzero_pl'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$result['quantity'] = $mdb -> get( 'pp_shop_products', 'quantity', [ 'id' => $product_id ] );
|
||||
$result['stock_0_buy'] = $mdb -> get( 'pp_shop_products', 'stock_0_buy', [ 'id' => $product_id] );
|
||||
$result['messages'] = $mdb -> get( 'pp_shop_products_langs', [ 'warehouse_message_zero', 'warehouse_message_nonzero' ], [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => 'pl' ] ] );
|
||||
|
||||
if ( !$result['messages']['warehouse_message_zero'] )
|
||||
$result['messages']['warehouse_message_zero'] = $settings['warehouse_message_zero_pl'];
|
||||
|
||||
if ( !$result['messages']['warehouse_message_nonzero'] )
|
||||
$result['messages']['warehouse_message_nonzero'] = $settings['warehouse_message_nonzero_pl'];
|
||||
}
|
||||
|
||||
$cacheHandler -> set( $cacheKey, $result );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// pobierz stan magazynowy produktu
|
||||
// FASADA - wywołuje nową klasę Domain\Product\ProductRepository
|
||||
static public function get_product_quantity( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
$repository = new \Domain\Product\ProductRepository($mdb);
|
||||
return $repository->getQuantity($product_id);
|
||||
}
|
||||
|
||||
public static function product_categories( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_shop_products_categories', 'category_id', [ 'product_id' => $product_id ] );
|
||||
}
|
||||
|
||||
public static function calculate_basket_product_price( float $price_brutto_promo, float $price_brutto, $coupon, $basket_position )
|
||||
{
|
||||
// ? produkty przecenione
|
||||
if ( $price_brutto_promo )
|
||||
{
|
||||
$price['price'] = $price_brutto;
|
||||
$price['price_new'] = $price_brutto_promo;
|
||||
|
||||
// ? zastosuje kod rabatowy
|
||||
if ( $coupon -> type && $coupon -> include_discounted_product )
|
||||
{
|
||||
// ? ograniczony do wybranych kategorii
|
||||
if ( $coupon -> categories != null )
|
||||
{
|
||||
$coupon_categories = json_decode( $coupon -> categories );
|
||||
$product_categories = \shop\Product::product_categories( (int)$basket_position['parent_id'] ? (int)$basket_position['parent_id'] : (int)$basket_position['product-id'] );
|
||||
if ( is_array( $coupon_categories ) ) foreach ( $coupon_categories 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ? nieograniczony kategoriami
|
||||
else
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon -> amount / 100 );
|
||||
|
||||
// ? uwzględnij promocję jeżeli może łączyć się z rabatami i produktami przecenionymi
|
||||
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
// ? najtańszy produkt w koszyku (z wybranych kategorii) za X zł
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
|
||||
// ? rabat procentowy
|
||||
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 );
|
||||
}
|
||||
}
|
||||
// ? brak kodu rabatowego
|
||||
else
|
||||
{
|
||||
if ( $basket_position['discount_amount'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
// ? Najtańszy produkt w koszyku (z wybranych kategorii) za X zł
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
|
||||
// ? rabat procentowy
|
||||
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;
|
||||
|
||||
// ? zastosuj kod rabatowy
|
||||
if ( $coupon -> type )
|
||||
{
|
||||
// ? ograniczony do wybranych kategorii
|
||||
if ( $coupon -> categories != null )
|
||||
{
|
||||
$coupon_categories = json_decode( $coupon -> categories );
|
||||
$product_categories = \shop\Product::product_categories( $basket_position['parent_id'] ? $basket_position['parent_id'] : $basket_position['product-id'] );
|
||||
if ( is_array( $coupon_categories ) ) foreach ( $coupon_categories 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ? nieograniczony
|
||||
else
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $coupon -> amount / 100 );
|
||||
|
||||
// ? uwzględnij promocję jeżeli może łączyć się z rabatami i produktami przecenionymi
|
||||
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
// ? najtańszy produkt w koszyku (z wybranych kategorii) za X zł
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
|
||||
// ? rabat procentowy
|
||||
if ( $basket_position['discount_type'] == 1 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $basket_position['discount_amount'] / 100 );
|
||||
}
|
||||
}
|
||||
// ? bez kodu rabatowego
|
||||
else
|
||||
{
|
||||
if ( $basket_position['discount_amount'] )
|
||||
{
|
||||
// ? Najtańszy produkt w koszyku (z wybranych kategorii) za X zł
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
|
||||
// ? rabat procentowy
|
||||
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;
|
||||
}
|
||||
|
||||
// pobierz wiadomość zależną od stanu magazynowego
|
||||
// id_product - id produktu
|
||||
// attributes - wybrane atrybuty przez użytkownika
|
||||
public static function getWarehouseMessage( int $id_product, $attributes, string $lang_id )
|
||||
{
|
||||
global $settings;
|
||||
|
||||
$permutation = self::getPermutation( $attributes );
|
||||
|
||||
$quantity = self::getPermutationQuantity( $id_product, $permutation );
|
||||
|
||||
if ( $quantity )
|
||||
{
|
||||
if ( $msg = ( new \Domain\Product\ProductRepository( $mdb ) )->getWarehouseMessageNonzero( (int)$id_product, $lang_id ) )
|
||||
$result = [ 'msg' => $msg, 'quantity' => $quantity ];
|
||||
else if ( $settings[ 'warehouse_message_nonzero_' . $lang_id ] )
|
||||
$result = [ 'msg' => $settings[ 'warehouse_message_nonzero_' . $lang_id ], 'quantity' => $quantity ];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $msg = ( new \Domain\Product\ProductRepository( $mdb ) )->getWarehouseMessageZero( (int)$id_product, $lang_id ) )
|
||||
$result = [ 'msg' => $msg, 'quantity' => $quantity ];
|
||||
else if ( $settings[ 'warehouse_message_zero_' . $lang_id ] )
|
||||
$result = [ 'msg' => $settings[ 'warehouse_message_zero_' . $lang_id ], 'quantity' => $quantity ];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// pobranie id produktu wg wybranych parametrów
|
||||
static public function get_product_id_by_attributes( int $parent_id, array $attributes )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
return $mdb -> get( 'pp_shop_products', 'id', [ 'AND' => [ 'parent_id' => $parent_id, 'permutation_hash' => implode( '|', $attributes ) ] ] );
|
||||
}
|
||||
|
||||
// pobranie permutation_hash dla kombinacji produktu
|
||||
static public function get_product_permutation_hash( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_shop_products', 'permutation_hash', [ 'id' => $product_id ] );
|
||||
}
|
||||
|
||||
// pobranie listy atrybutów z wybranymi wartościami
|
||||
static public function get_product_attributes( $products )
|
||||
{
|
||||
if ( !is_array( $products ) or !count( $products ) )
|
||||
return false;
|
||||
|
||||
$attributes = array();
|
||||
|
||||
foreach ( $products as $product )
|
||||
{
|
||||
$permutations = explode( '|', $product['permutation_hash'] );
|
||||
foreach ( $permutations as $permutation )
|
||||
{
|
||||
$attribute = explode( '-', $permutation );
|
||||
|
||||
$value['id'] = $attribute[1];
|
||||
$value['is_default'] = \shop\ProductAttribute::is_value_default( $attribute[1] );
|
||||
|
||||
if ( array_search( $attribute[1], array_column( $attributes, 'id' ) ) === false )
|
||||
$attributes[ $attribute[0] ][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$attributes = \Shared\Helpers\Helpers::removeDuplicates( $attributes, 'id' );
|
||||
|
||||
foreach ( $attributes as $key => $val )
|
||||
{
|
||||
$row['id'] = $key;
|
||||
$row['values'] = $val;
|
||||
|
||||
$attributes_sort[ \shop\ProductAttribute::get_attribute_order( $key ) ] = $row;
|
||||
}
|
||||
|
||||
return $attributes_sort;
|
||||
}
|
||||
|
||||
public static function array_cartesian( $input )
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ( $input as $key => $values )
|
||||
{
|
||||
if ( empty( $values ) )
|
||||
continue;
|
||||
|
||||
if ( empty( $result ) )
|
||||
{
|
||||
foreach ( $values as $value )
|
||||
{
|
||||
$result[] = array( $key => $value );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$append = array();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function __get( $variable )
|
||||
{
|
||||
if ( is_array( $this -> data ) and array_key_exists( $variable, $this -> data ) )
|
||||
return $this -> $variable;
|
||||
}
|
||||
|
||||
public function __set( $variable, $value )
|
||||
{
|
||||
$this -> $variable = $value;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
|
||||
// generate_sku_code
|
||||
public static function generate_sku_code( int $product_id )
|
||||
{
|
||||
global $mdb;
|
||||
// find product with sku like 'PP-000000'
|
||||
$skus = $mdb -> select( 'pp_shop_products', 'sku', [ 'sku[~]' => 'PP-' ] );
|
||||
if ( is_array( $skus ) )
|
||||
{
|
||||
foreach ( $skus as $sku )
|
||||
$sku_codes[] = (int)substr( $sku, 3 );
|
||||
}
|
||||
|
||||
// find max sku
|
||||
if ( is_array( $sku_codes ) )
|
||||
$sku = 'PP-' . str_pad( max( $sku_codes ) + 1, 6, '0', STR_PAD_LEFT );
|
||||
else
|
||||
$sku = 'PP-000001';
|
||||
|
||||
return $sku;
|
||||
}
|
||||
|
||||
// product_xml_name_save
|
||||
public static function product_xml_name_save( int $product_id, string $xml_name, string $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_shop_products_langs', [ 'xml_name' => $xml_name ], [ 'AND' => [ 'product_id' => $product_id, 'lang_id' => $lang_id ] ] );
|
||||
}
|
||||
|
||||
// product_custom_label_suggestions
|
||||
public static function product_custom_label_suggestions( string $custom_label, string $label_type )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$results = $mdb -> query( 'SELECT DISTINCT
|
||||
' . $label_type . ' AS label
|
||||
FROM
|
||||
pp_shop_products
|
||||
WHERE
|
||||
' . $label_type . ' LIKE :custom_label
|
||||
LIMIT 10', [ ':custom_label' => '%' . $custom_label . '%' ] ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||
if ( is_array( $results ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
$output[] = $row;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
// product_custom_label_save
|
||||
public static function product_custom_label_save( int $product_id, string $custom_label, string $label_type )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> update( 'pp_shop_products', [ $label_type => $custom_label ], [ 'id' => $product_id ] );
|
||||
}
|
||||
public static function product_meta (int $product_id)
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->select(
|
||||
"pp_shop_products_categories (ppc)",
|
||||
[
|
||||
"[>]pp_shop_categories_langs (pcl)" => ["ppc.category_id" => "category_id"]
|
||||
],
|
||||
[
|
||||
"pcl.title",
|
||||
"pcl.seo_link"
|
||||
],
|
||||
[
|
||||
"ppc.product_id" => $product_id
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class ProductAttribute implements \ArrayAccess
|
||||
{
|
||||
// sprawdź czy wartość atrybutu jest domyślna
|
||||
static public function is_value_default( int $value_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\ProductAttribute::is_value_default:$value_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$is_default = $mdb -> get( 'pp_shop_attributes_values', 'is_default', [ 'id' => $value_id ] );
|
||||
$cacheHandler -> set( $cacheKey, $is_default );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $is_default;
|
||||
}
|
||||
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->$offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
public function offsetSet($offset , $value) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->$offset);
|
||||
}
|
||||
|
||||
public function __construct( int $attribute_id, $lang_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( !$lang_id )
|
||||
$lang_id = ( new \Domain\Languages\LanguagesRepository( $mdb ) )->defaultLanguage();
|
||||
|
||||
$result = $mdb -> get( 'pp_shop_attributes', '*', [ 'id' => $attribute_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
|
||||
$results = $mdb -> select( 'pp_shop_attributes_langs', '*', [ 'AND' => [ 'attribute_id' => $attribute_id, 'lang_id' => $lang_id ] ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
$this -> language = $row;
|
||||
}
|
||||
|
||||
// pobierz kolejność atrybutu
|
||||
static public function get_attribute_order( int $attribute_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\ProductAttribute::get_attribute_order:$attribute_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$order = $mdb -> get( 'pp_shop_attributes', 'o', [ 'id' => $attribute_id ] );
|
||||
$cacheHandler -> set( $cacheKey, $order );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
// pobierz nazwę atrybutu za pomocą wartości
|
||||
static public function getAttributeNameByValue( int $value_id, string $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
return array_pop( $mdb -> 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 . '\'' ) -> fetch( \PDO::FETCH_ASSOC ) );
|
||||
}
|
||||
|
||||
// pobierz nazwę wartości
|
||||
static public function get_value_name( int $value_id, string $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\ProductAttribute::get_value_name:$value_id:$lang_id";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$value_name = $mdb -> get( 'pp_shop_attributes_values_langs', 'name', [ 'AND' => [ 'value_id' => (int)$value_id, 'lang_id' => $lang_id ] ] );
|
||||
$cacheHandler -> set( $cacheKey, $value_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize($objectData);
|
||||
}
|
||||
return $value_name;
|
||||
}
|
||||
|
||||
// pobierz nazwę atrybutu
|
||||
static public function getAttributeName( int $attribute_id, string $lang_id )
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> get( 'pp_shop_attributes_langs', 'name', [ 'AND' => [ 'attribute_id' => (int)$attribute_id, 'lang_id' => $lang_id ] ] );
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class ProductCustomField implements \ArrayAccess
|
||||
{
|
||||
public function __construct( int $custom_field_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$result = $mdb -> get( 'pp_shop_products_custom_fields', '*', [ 'id_additional_field' => $custom_field_id ] );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $result ) ) foreach ( $result as $key => $val )
|
||||
$this -> $key = $val;
|
||||
}
|
||||
|
||||
static public function getFromCache( int $custom_field_id )
|
||||
{
|
||||
// Check if Redis extension is loaded
|
||||
if ( class_exists( 'Redis' ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance() -> getConnection();
|
||||
|
||||
if ( $redis )
|
||||
{
|
||||
$objectData = $redis -> get( "shop\ProductCustomField:$custom_field_id" );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$object = new self( $custom_field_id );
|
||||
$redis -> setex( "shop\ProductCustomField:$custom_field_id", 60 * 60 * 24, serialize( $object ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$object = unserialize( $objectData );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log the error if needed
|
||||
$object = new self( $custom_field_id );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
// Log the exception if needed
|
||||
$object = new self( $custom_field_id );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Redis extension not loaded, create a new instance
|
||||
$object = new self( $custom_field_id );
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->$offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
public function offsetSet($offset , $value) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->$offset);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class ProductSet implements \ArrayAccess
|
||||
{
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->$offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return $this->$offset;
|
||||
}
|
||||
|
||||
public function offsetSet($offset , $value) {
|
||||
$this->$offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->$offset);
|
||||
}
|
||||
|
||||
public function __construct( int $set_id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$repo = new \Domain\ProductSet\ProductSetRepository( $mdb );
|
||||
$data = $repo->find( $set_id );
|
||||
|
||||
foreach ( $data as $key => $val )
|
||||
$this->$key = $val;
|
||||
}
|
||||
|
||||
//lista dostepnych kompletow (fasada do repozytorium)
|
||||
static public function sets_list()
|
||||
{
|
||||
global $mdb;
|
||||
$repo = new \Domain\ProductSet\ProductSetRepository( $mdb );
|
||||
return $repo->allSets();
|
||||
}
|
||||
|
||||
// usuwanie kompletu produktow (fasada do repozytorium)
|
||||
static public function set_delete( int $set_id )
|
||||
{
|
||||
global $mdb;
|
||||
$repo = new \Domain\ProductSet\ProductSetRepository( $mdb );
|
||||
return $repo->delete( $set_id );
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
|
||||
class Promotion
|
||||
{
|
||||
private $data = [];
|
||||
|
||||
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( $id )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $id )
|
||||
{
|
||||
$result = $mdb->get( 'pp_shop_promotion', '*', [ 'id' => $id ] );
|
||||
if ( is_array( $result ) )
|
||||
$this->data = $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function __get( $variable )
|
||||
{
|
||||
if ( isset( $this->data[$variable] ) )
|
||||
return $this->data[$variable];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function get_active_promotions()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "\shop\Promotion::get_active_promotions";
|
||||
|
||||
$objectData = $cacheHandler -> get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$results = $mdb -> 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 static function find_promotion( $basket )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
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 = self::get_active_promotions();
|
||||
if ( is_array( $results ) and count( $results ) )
|
||||
{
|
||||
$promoRepo = new \Domain\Promotion\PromotionRepository( $mdb );
|
||||
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$promotion = new \shop\Promotion( $row );
|
||||
|
||||
// Promocja na cały koszyk
|
||||
if ( $promotion -> condition_type == 4 )
|
||||
return $promoRepo->applyTypeWholeBasket( $basket_tmp, $promotion );
|
||||
|
||||
// Najtańszy produkt w koszyku (z wybranych kategorii) za X zł
|
||||
if ( $promotion -> condition_type == 3 )
|
||||
return $promoRepo->applyTypeCheapestProduct( $basket_tmp, $promotion );
|
||||
|
||||
// Rabat procentowy na produkty z kategorii 1 lub kategorii 2
|
||||
if ( $promotion -> condition_type == 5 )
|
||||
return $promoRepo->applyTypeCategoriesOr( $basket_tmp, $promotion );
|
||||
|
||||
// Rabat procentowy na produkty z kategorii I i kategorii II
|
||||
if ( $promotion -> condition_type == 2 )
|
||||
return $promoRepo->applyTypeCategoriesAnd( $basket_tmp, $promotion );
|
||||
|
||||
// Rabat procentowy na produkty z kategorii I jeżeli w koszyku jest produkt z kategorii II
|
||||
if ( $promotion -> condition_type == 1 )
|
||||
return $promoRepo->applyTypeCategoryCondition( $basket_tmp, $promotion );
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
namespace shop;
|
||||
use shop\Produt;
|
||||
|
||||
class Search implements \ArrayAccess
|
||||
{
|
||||
static public function simple_form()
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'shop-search/simple-form' );
|
||||
}
|
||||
|
||||
static public function search_results()
|
||||
{
|
||||
global $lang_id;
|
||||
|
||||
$bs = \Shared\Helpers\Helpers::get( 'bs' );
|
||||
|
||||
$results = \shop\Product::searchProductsByName( \Shared\Helpers\Helpers::get( 'query' ), $lang_id, (int)$bs );
|
||||
|
||||
$out = \Shared\Tpl\Tpl::view( 'shop-search/products', [
|
||||
'query' => \Shared\Helpers\Helpers::get( 'query' ),
|
||||
'products' => $results['products']
|
||||
]);
|
||||
|
||||
if ( $results['ls'] > 1 )
|
||||
{
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> ls = $results['ls'];
|
||||
$tpl -> bs = $bs ? $bs : 1;
|
||||
$tpl -> link = 'wyszukiwarka/' . \Shared\Helpers\Helpers::get( 'query' );
|
||||
$out .= $tpl -> render( 'site/pager' );
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
static public function search_products()
|
||||
{
|
||||
global $lang_id;
|
||||
|
||||
$results = \shop\Product::searchProductByNameAjax( \Shared\Helpers\Helpers::get( 'query' ), $lang_id );
|
||||
if ( \Shared\Helpers\Helpers::is_array_fix( $results ) ) foreach ( $results as $row )
|
||||
$products[] = \Shared\Tpl\Tpl::view( 'shop-search/product-search', [
|
||||
'product' => Product::getFromCache( $row['product_id'], $lang_id )
|
||||
] );
|
||||
|
||||
echo json_encode( $products );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?
|
||||
namespace shop;
|
||||
class Shop implements \ArrayAccess
|
||||
{
|
||||
static public function shortPrice( $price )
|
||||
{
|
||||
if ( self::isWholeNumber( $price ) )
|
||||
$price = round( $price, 0 );
|
||||
else
|
||||
$price = \Shared\Helpers\Helpers::decimal( $price );
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
static public function isWholeNumber( $value )
|
||||
{
|
||||
return (is_numeric($value) && (round($value, 3) == round($value)));
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?
|
||||
namespace shop;
|
||||
class Transport implements \ArrayAccess
|
||||
{
|
||||
// lista dostępnych form wysyłki
|
||||
static public function transport_list()
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb -> select( 'pp_shop_transports', '*', [ 'status' => 1 ], [ 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return isset( $this -> $offset );
|
||||
}
|
||||
|
||||
public function offsetGet( $offset )
|
||||
{
|
||||
return $this -> $offset;
|
||||
}
|
||||
|
||||
public function offsetSet( $offset, $value )
|
||||
{
|
||||
$this -> $offset = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset( $offset )
|
||||
{
|
||||
unset( $this -> $offset );
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
use shop\Order;
|
||||
|
||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||
|
||||
function __autoload_my_classes( $classname )
|
||||
@@ -79,7 +77,7 @@ if ( is_array( $order_id ) and $order_id['id'] )
|
||||
{
|
||||
'local_id': <?= (int)$product['product_id'];?>,
|
||||
'name': '<?= $product['name'];?>',
|
||||
'product_url': 'https://pomysloweprezenty.pl<?= \front\Views\ShopProduct::productUrl( \shop\Product::getFromCache( $product['product_id'], 'pl' ) );?>',
|
||||
'product_url': 'https://pomysloweprezenty.pl<?= \front\Views\ShopProduct::productUrl( ( new \Domain\Product\ProductRepository( $mdb ) )->findCached( $product['product_id'], 'pl' ) );?>',
|
||||
"image_url": "https://pomysloweprezenty.pl<?= ( new \Domain\Product\ProductRepository( $mdb ) )->getFirstImageCached( (int)$product['product_id'] );?>"
|
||||
}<? if ( $product != end( $order['products'] ) ) echo ',';?>
|
||||
<? endforeach;?>
|
||||
|
||||
12
cron.php
12
cron.php
@@ -1,6 +1,4 @@
|
||||
<?php
|
||||
use shop\Order;
|
||||
|
||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||
|
||||
function __autoload_my_classes( $classname )
|
||||
@@ -60,7 +58,9 @@ $apilo_settings = $integrationsRepository -> getSettings( 'apilo' );
|
||||
if ( (int)($apilo_settings['enabled'] ?? 0) === 1 ) {
|
||||
$integrationsRepository -> apiloKeepalive( 300 );
|
||||
$apilo_settings = $integrationsRepository -> getSettings( 'apilo' );
|
||||
Order::process_apilo_sync_queue( 10 );
|
||||
$orderRepo = new \Domain\Order\OrderRepository( $mdb );
|
||||
$orderAdminService = new \Domain\Order\OrderAdminService( $orderRepo );
|
||||
$orderAdminService->processApiloSyncQueue( 10 );
|
||||
}
|
||||
|
||||
function parsePaczkomatAddress($input)
|
||||
@@ -521,12 +521,10 @@ if ( $apilo_settings['enabled'] and $apilo_settings['sync_orders'] and $apilo_se
|
||||
{
|
||||
$shop_status_id = ( new \Domain\ShopStatus\ShopStatusRepository( $mdb ) )->getByIntegrationStatusId( 'apilo', (int)$responseData['status'] );
|
||||
|
||||
$order_tmp = new Order( $order['id'] );
|
||||
|
||||
if ( $shop_status_id )
|
||||
$order_tmp -> update_status( $shop_status_id, false );
|
||||
$orderAdminService->changeStatus( (int)$order['id'], $shop_status_id, false );
|
||||
|
||||
$order_tmp -> update_aplio_order_status_date( date( 'Y-m-d H:i:s' ) );
|
||||
$orderRepo->updateApiloStatusDate( (int)$order['id'], date( 'Y-m-d H:i:s' ) );
|
||||
echo '<p>Zaktualizowałem status zamówienia <b>' . $order['number'] . '</b></p>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,35 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.294 (2026-02-18) - Usuniecie autoload/shop/ — 12 legacy klas
|
||||
|
||||
- **Faza 5.1: class.Order.php (~562 linii) USUNIETA**
|
||||
- Logika Apilo sync przeniesiona do `OrderAdminService::processApiloSyncQueue()`
|
||||
- Email zmiany statusu zintegrowany w `OrderAdminService::changeStatus()`
|
||||
- `cron.php` przepiety na `OrderAdminService`
|
||||
- `front\Controllers\ShopOrderController` — dodano `OrderAdminService` jako drugie DI
|
||||
- `\shop\Order::order_statuses()` → `OrderRepository::orderStatuses()` (3 lokalizacje)
|
||||
- **Faza 5.2: class.Product.php (~952 linii) USUNIETA — NAJWIEKSZA KLASA**
|
||||
- ~20 metod przeniesionych do `ProductRepository`: `findCached()`, `isProductOnPromotion()`, `productSetsWhenAddToBasket()`, `addVisit()`, `getProductImg()`, `getProductUrl()`, `searchProductsByName()`, `searchProductByNameAjax()`, `searchProductsByNameCount()`, `isStock0Buy()`, `getProductPermutationQuantityOptions()`, `getProductIdByAttributes()`, `getProductPermutationHash()`, `getProductAttributes()`, `generateSkuCode()`, `productMeta()`, `generateSubtitleFromAttributes()`, `getDefaultCombinationPrices()`, `getProductDataBySelectedAttributes()`, `productCategories()`, `arrayCartesian()`
|
||||
- `calculate_basket_product_price()` przeniesione do `BasketCalculator::calculateBasketProductPrice()`
|
||||
- `BasketCalculator` przepisany: `summaryPrice()`, `checkProductQuantityInStock()` uzywaja `ProductRepository`
|
||||
- XML generation (4 metody) — konwersja object→array access
|
||||
- ~60+ callsite'ow zamienionych w kontrolerach, szablonach, repozytoriach i entry-pointach
|
||||
- Cache key strings zaktualizowane z `\shop\Product::` na `ProductRepository::`
|
||||
- **Cleanup:**
|
||||
- Usunieto dead `\shop\` routing z `front\App::route()`
|
||||
- Cache key `\shop\Promotion::get_active_promotions` → `PromotionRepository::getActivePromotions`
|
||||
- Katalog `autoload/shop/` jest teraz pusty
|
||||
- **Production hotfixes:**
|
||||
- FIX: `findCached()` — stale Redis cache z obiektami `\shop\Product` powodowal ceny 0,00 zl (obiekty `__PHP_Incomplete_Class` po `(array)` cast maja zmanglowane klucze). Teraz invaliduje stary cache i re-fetchuje z DB
|
||||
- FIX: `LayoutEngine.php` — 4 niekwalifikowane `Product::getFromCache()` resolwaly do `\front\Product` po usunieciu `use shop\Product`
|
||||
- FIX: szablony (product-mini, product-search, product, product-warehouse-message, alert-product-sets, basket-details, main-view, promoted-products) — konwersja object access (`$product->field`) na array access (`$product['field']`)
|
||||
- UPDATE: `AttributeRepository::getAttributeValueById()` — dodano Redis cache (zgodnosc ze starym `\shop\ProductAttribute::get_value_name()`)
|
||||
- **PODSUMOWANIE CALEJ MIGRACJI:** 12 legacy klas, ~2363 linii kodu usunieto. Zero referencji `\shop\` w aktywnym kodzie.
|
||||
- Testy: 610 OK, 1817 asercji
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.293 (2026-02-17) - front\controls\Site + front\view\Site → front\App + front\LayoutEngine
|
||||
|
||||
- **front\controls\Site → front\App** — migracja routera na nowy namespace
|
||||
|
||||
234
docs/LEGACY_SHOP_REFACTORING_PLAN.md
Normal file
234
docs/LEGACY_SHOP_REFACTORING_PLAN.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# Plan refaktoryzacji `autoload/shop/` — usunięcie legacy klas
|
||||
|
||||
## Kontekst
|
||||
|
||||
Katalog `autoload/shop/` zawierał **12 legacy klas** (~2 363 linii kodu), które pełniły rolę fasad/wrapperów nad warstwą `Domain\`. Wszystkie klasy zostały usunięte — logika przeniesiona do warstwy `Domain\` / `Shared\`, wywołania zaktualizowane.
|
||||
|
||||
**Status: UKOŃCZONE** (wszystkie 12 klas usunięte, katalog `autoload/shop/` pusty)
|
||||
|
||||
## Status
|
||||
|
||||
| Faza | Klasy | Status |
|
||||
|------|-------|--------|
|
||||
| 1 | Transport, ProductSet, Coupon | ✅ Ukończona |
|
||||
| 2 | Shop, Search, Basket | ✅ Ukończona |
|
||||
| 3 | ProductCustomField, Category, ProductAttribute | ✅ Ukończona |
|
||||
| 4 | Promotion | ✅ Ukończona |
|
||||
| 5 | Order, Product | ✅ Ukończona |
|
||||
|
||||
---
|
||||
|
||||
## Faza 1 — Trywialne fasady (0 logiki do migracji)
|
||||
|
||||
### 1.1 `class.Transport.php` ✅ (~31 linii)
|
||||
**Jedyne zewnętrzne użycie:**
|
||||
- `admin\Controllers\ShopOrderController.php` → `\shop\Transport::transport_list()`
|
||||
|
||||
**Plan:**
|
||||
- Dodać metodę `allActiveOrdered()` do `Domain\Transport\TransportRepository` (odpowiednik `transport_list()`)
|
||||
- Zamienić wywołanie w `ShopOrderController`
|
||||
- Usunąć `class.Transport.php`
|
||||
|
||||
### 1.2 `class.ProductSet.php` ✅ (~49 linii)
|
||||
**Jedyne zewnętrzne użycie:**
|
||||
- `admin\Controllers\ShopProductController.php:224` → `\shop\ProductSet::sets_list()`
|
||||
|
||||
**Plan:**
|
||||
- `ProductSetRepository::allSets()` już istnieje — zamienić wywołanie na `$this->productSetRepo->allSets()`
|
||||
- Wstrzyknąć `ProductSetRepository` do `ShopProductController` (jeśli jeszcze nie jest)
|
||||
- Usunąć `class.ProductSet.php`
|
||||
|
||||
### 1.3 `class.Coupon.php` ✅ (~84 linii)
|
||||
**Zewnętrzne użycia (3×):**
|
||||
- `shop\class.Order.php:171` → `new \shop\Coupon($id)` (do przeniesienia w fazie Order)
|
||||
- `front\Controllers\ShopOrderController.php:139` → `new \shop\Coupon($id)`
|
||||
- `admin\Controllers\ShopOrderController.php:170` → `new \shop\Coupon($id)`
|
||||
|
||||
**Plan:**
|
||||
- `CouponRepository::findByName()` / `find()` już istnieje — dodać `findById(int $id): ?array` jeśli brak
|
||||
- Zamienić `new \shop\Coupon($id)` → `$this->couponRepo->findById($id)` w obu kontrolerach
|
||||
- Użycie w `class.Order.php` — rozwiąże się w fazie Order
|
||||
- Usunąć `class.Coupon.php`
|
||||
|
||||
---
|
||||
|
||||
## Faza 2 — Proste wrappery (minimalna logika do przeniesienia)
|
||||
|
||||
### 2.1 `class.Shop.php` ✅ (~39 linii) — utility cenowe
|
||||
**Zewnętrzne użycia (~24× w szablonach):**
|
||||
- `templates/shop-product/product.php`, `product-mini.php`
|
||||
- `templates/shop-search/product-search.php`
|
||||
- `templates/shop-basket/alert-product-sets.php`
|
||||
- `templates/controls/alert-product-sets.php`
|
||||
|
||||
**Plan:**
|
||||
- Przenieść `shortPrice()` i `isWholeNumber()` do `Shared\Helpers\Helpers` jako metody statyczne
|
||||
- Zamienić `\shop\Shop::shortPrice()` → `\Shared\Helpers\Helpers::shortPrice()` we wszystkich szablonach (replace_all)
|
||||
- Usunąć `class.Shop.php`
|
||||
|
||||
### 2.2 `class.Search.php` ✅ (~70 linii)
|
||||
**Jedyne zewnętrzne użycie:**
|
||||
- `front\LayoutEngine.php:337` → `\shop\Search::simple_form()`
|
||||
|
||||
**Plan:**
|
||||
- Przenieść `simple_form()` do `front\Views\ShopSearch` (nowa klasa View, statyczna)
|
||||
- Przenieść `search_results()` i `search_products()` do `front\Controllers\ShopSearchController` lub odpowiedniej istniejącej lokalizacji
|
||||
- Zamienić wywołanie w `LayoutEngine`
|
||||
- Usunąć `class.Search.php`
|
||||
- Poprawić bug: `use shop\Produt;` (literówka — brakuje 'c')
|
||||
|
||||
### 2.3 `class.Basket.php` ✅ (~92 linii)
|
||||
**Zewnętrzne użycia (6× w `ShopBasketController`):**
|
||||
- `validate_basket()` — prosta walidacja tablicy sesji
|
||||
- `check_product_quantity_in_stock()` — sprawdzenie stanów magazynowych
|
||||
|
||||
**Plan:**
|
||||
- Przenieść obie metody do `Domain\Basket\BasketCalculator` (już istnieje)
|
||||
- Zamienić wywołania w `ShopBasketController`
|
||||
- Usunąć `class.Basket.php`
|
||||
|
||||
---
|
||||
|
||||
## Faza 3 — Klasy z logiką cache/atrybutów
|
||||
|
||||
### 3.1 `class.ProductCustomField.php` ✅ (~74 linii)
|
||||
**Zewnętrzne użycia (2×):**
|
||||
- `Domain\Order\OrderRepository.php:643` → `\shop\ProductCustomField::getFromCache()`
|
||||
- `templates/shop-basket/_partials/product-custom-fields.php:3` → `\shop\ProductCustomField::getFromCache()`
|
||||
|
||||
**Plan:**
|
||||
- Dodać `findCached(int $id): ?array` do `Domain\Product\ProductRepository` (lub nowy `ProductCustomFieldRepository`)
|
||||
- Zamienić wywołania w OrderRepository i szablonie
|
||||
- Usunąć `class.ProductCustomField.php`
|
||||
|
||||
### 3.2 `class.Category.php` ✅ (~84 linii)
|
||||
**Zewnętrzne użycia (2×):**
|
||||
- `index.php:225` → `\shop\Category::get_category_products_id()` (FB Pixel)
|
||||
- `templates/shop-category/category.php:19` → `\shop\Category::get_subcategory_by_category()`
|
||||
|
||||
**Plan:**
|
||||
- `CategoryRepository::productsId()` prawdopodobnie istnieje — sprawdzić i użyć lub dodać
|
||||
- Dodać `subcategoriesCached(int $categoryId): array` do `CategoryRepository`
|
||||
- Zamienić wywołania
|
||||
- Usunąć `class.Category.php`
|
||||
|
||||
### 3.3 `class.ProductAttribute.php` ✅ (~119 linii)
|
||||
**Zewnętrzne użycia (3 lokalizacje):**
|
||||
- `shop\class.Product.php` — użycia wewnętrzne (rozwiążą się w fazie Product)
|
||||
- `admin/templates/shop-product/product-combination.php:32` → `getAttributeName()`, `get_value_name()`
|
||||
- `templates/shop-product/_partial/product-attribute.php:13` → `get_value_name()`
|
||||
|
||||
**Plan:**
|
||||
- `AttributeRepository::getAttributeNameById()` i `getAttributeValueById()` już istnieją
|
||||
- Dodać brakujące metody do `AttributeRepository`: `isValueDefault()`, `getAttributeOrder()`, `getAttributeNameByValue()` (z Redis cache)
|
||||
- Zamienić wywołania w szablonach
|
||||
- Usunąć `class.ProductAttribute.php`
|
||||
|
||||
---
|
||||
|
||||
## Faza 4 — Klasy z logiką promocji
|
||||
|
||||
### 4.1 `class.Promotion.php` ✅ (~107 linii)
|
||||
**Zewnętrzne użycia (3 lokalizacje):**
|
||||
- `front\Controllers\ShopBasketController.php` (6×) → `\shop\Promotion::find_promotion()`
|
||||
- `admin\Controllers\ShopPromotionController.php` → `::$condition_type`, `::$discount_type`
|
||||
|
||||
**Plan:**
|
||||
- Przenieść `get_active_promotions()` do `PromotionRepository` (z Redis cache)
|
||||
- Przenieść `find_promotion()` do `PromotionRepository` — orkiestracja logiki applyType*
|
||||
- Przenieść stałe `$condition_type` / `$discount_type` jako stałe klasowe do `PromotionRepository`
|
||||
- Zamienić wywołania w `ShopBasketController` i `ShopPromotionController`
|
||||
- Usunąć `class.Promotion.php`
|
||||
|
||||
---
|
||||
|
||||
## Faza 5 — Duże klasy z wieloma zależnościami
|
||||
|
||||
### 5.1 `class.Order.php` ✅ (~562 linii)
|
||||
**Główna logika do migracji:**
|
||||
- `OrderAdminService` ma już: `setOrderAsPaid()`, `setOrderAsUnpaid()`, `changeStatus()`, `resendConfirmationEmail()`, `saveOrderByAdmin()`, `deleteOrder()`, `saveNotes()`
|
||||
- **Brakuje:** logika Apilo sync (`process_apilo_sync_queue()`, `sync_apilo_payment()`, `sync_apilo_status()`, queue management)
|
||||
- **Brakuje:** `send_status_change_email()` (ale może być zintegrowane w `changeStatus()`)
|
||||
|
||||
**Plan:**
|
||||
1. Sprawdzić, czy `OrderAdminService` obsługuje już Apilo sync (metoda `sendOrderToApilo()` istnieje)
|
||||
2. Przenieść `process_apilo_sync_queue()` + kolejkę Apilo do `Domain\Integrations\ApiloService` (lub do `IntegrationsRepository`)
|
||||
3. Przenieść `send_status_change_email()` do `OrderAdminService` (jeśli nie jest częścią `changeStatus()`)
|
||||
4. Usunąć referencję do `new \shop\Coupon()` — użyć `CouponRepository` (z fazy 1.3)
|
||||
5. Zaktualizować `cron.php` (jeśli woła `process_apilo_sync_queue`)
|
||||
6. Usunąć `class.Order.php`
|
||||
|
||||
### 5.2 `class.Product.php` ✅ (~952 linii) — NAJWIĘKSZA KLASA
|
||||
**Stan:** Większość logiki zmigowana do `ProductRepository`, ale klasa pełni rolę entity z ArrayAccess (używana w szablonach jako `$product['name']`).
|
||||
|
||||
**Metody do przeniesienia:**
|
||||
- `getFromCache()` — cache wrappera entity → przenieść do `ProductRepository::findCached()`
|
||||
- `getDefaultCombinationPrices()` → `ProductRepository`
|
||||
- `generateSubtitleFromAttributes()` → `ProductRepository`
|
||||
- `getProductDataBySelectedAttributes()` → `ProductRepository`
|
||||
- `is_product_on_promotion()` → `ProductRepository`
|
||||
- `product_sets_when_add_to_basket()` → `ProductSetRepository`
|
||||
- `add_visit()` → `ProductRepository`
|
||||
- `getProductImg()` / `getProductUrl()` → `ProductRepository`
|
||||
- `searchProductsByName()` / `searchProductByNameAjax()` / `searchProductsByNameCount()` → `ProductRepository`
|
||||
- `is_stock_0_buy()` → `ProductRepository`
|
||||
- `get_product_permutation_quantity_options()` → `ProductRepository`
|
||||
- `calculate_basket_product_price()` → `Domain\Basket\BasketCalculator`
|
||||
- `get_product_id_by_attributes()` / `get_product_permutation_hash()` / `get_product_attributes()` → `ProductRepository` lub `AttributeRepository`
|
||||
- `array_cartesian()` / `permutations()` → `ProductRepository` (helper prywatny)
|
||||
- `generate_sku_code()` → `ProductRepository`
|
||||
- `product_meta()` → `ProductRepository`
|
||||
|
||||
**Kluczowy problem:** szablony używają `$product['field']` przez ArrayAccess. Po migracji szablony będą dostawać zwykłe `array` z `ProductRepository` — to jest kompatybilne, bo `$product['field']` działa tak samo na tablicy.
|
||||
|
||||
**Plan:**
|
||||
1. Etapami przenosić metody statyczne do `ProductRepository` / `AttributeRepository`
|
||||
2. Przenieść `calculate_basket_product_price()` do `BasketCalculator`
|
||||
3. Zamienić `Product::getFromCache()` → `ProductRepository::findCached()` (zwraca tablicę)
|
||||
4. Zaktualizować szablony i kontrolery
|
||||
5. Usunąć `class.Product.php`
|
||||
|
||||
---
|
||||
|
||||
## Pliki do modyfikacji (podsumowanie)
|
||||
|
||||
| Plik | Zmiany |
|
||||
|------|--------|
|
||||
| `autoload/Domain/Transport/TransportRepository.php` | + `allActiveOrdered()` |
|
||||
| `autoload/Domain/Basket/BasketCalculator.php` | + `validateBasket()`, `checkStock()`, `calculateProductPrice()` |
|
||||
| `autoload/Domain/Promotion/PromotionRepository.php` | + `getActivePromotions()`, `findPromotion()`, stałe |
|
||||
| `autoload/Domain/Product/ProductRepository.php` | + ~15 metod z class.Product.php |
|
||||
| `autoload/Domain/Attribute/AttributeRepository.php` | + `isValueDefault()`, `getAttributeOrder()`, `getAttributeNameByValue()` |
|
||||
| `autoload/Domain/Category/CategoryRepository.php` | + `subcategoriesCached()`, `categoryProductIds()` |
|
||||
| `autoload/Domain/Order/OrderAdminService.php` | + email statusu, sprawdzenie Apilo |
|
||||
| `autoload/Domain/Integrations/` | + `ApiloSyncService` (queue Apilo) |
|
||||
| `autoload/Domain/Coupon/CouponRepository.php` | + `findById()` |
|
||||
| `autoload/Shared/Helpers/Helpers.php` | + `shortPrice()`, `isWholeNumber()` |
|
||||
| `autoload/front/Views/ShopSearch.php` | NOWY — `simple_form()` |
|
||||
| `autoload/front/LayoutEngine.php` | zamiana `\shop\Search` → `\front\Views\ShopSearch` |
|
||||
| `autoload/front/Controllers/ShopBasketController.php` | zamiana `\shop\Basket`, `\shop\Promotion` |
|
||||
| `autoload/admin/Controllers/ShopOrderController.php` | zamiana `\shop\Coupon`, `\shop\Transport` |
|
||||
| `autoload/admin/Controllers/ShopProductController.php` | zamiana `\shop\ProductSet` |
|
||||
| `autoload/admin/Controllers/ShopPromotionController.php` | zamiana `::$condition_type`, `::$discount_type` |
|
||||
| `autoload/front/Controllers/ShopOrderController.php` | zamiana `\shop\Coupon` |
|
||||
| Szablony (`templates/`) | zamiana `\shop\Shop::shortPrice()`, `\shop\ProductAttribute::*`, `\shop\Category::*` |
|
||||
| `index.php` | zamiana `\shop\Category::get_category_products_id()` |
|
||||
| `autoload/Domain/Order/OrderRepository.php` | zamiana `\shop\ProductCustomField` |
|
||||
|
||||
## Weryfikacja
|
||||
|
||||
Po każdej fazie:
|
||||
1. Uruchomić `./test.ps1` — wszystkie 610+ testów muszą przechodzić
|
||||
2. Grep po `\shop\` w całym codebase — upewnić się, że usunięta klasa nie jest nigdzie używana
|
||||
3. Po zakończeniu wszystkich faz: `grep -r "\\\\shop\\\\" autoload/ templates/ admin/ index.php ajax.php cron.php api.php` powinien zwracać 0 wyników
|
||||
|
||||
## Szacunek złożoności
|
||||
|
||||
| Faza | Klasy | Linii do usunięcia | Złożoność |
|
||||
|------|-------|---------------------|-----------|
|
||||
| 1 | Transport, ProductSet, Coupon | ~164 | Niska |
|
||||
| 2 | Shop, Search, Basket | ~201 | Niska-średnia |
|
||||
| 3 | ProductCustomField, Category, ProductAttribute | ~277 | Średnia |
|
||||
| 4 | Promotion | ~107 | Średnia |
|
||||
| 5 | Order, Product | ~1 514 | Wysoka |
|
||||
| **Razem** | **12 klas** | **~2 363** | |
|
||||
@@ -38,23 +38,23 @@ Dokumentacja struktury projektu shopPRO do szybkiego odniesienia.
|
||||
```
|
||||
shop\product:{product_id}:{lang_id}:{permutation_hash}
|
||||
```
|
||||
- Przechowuje zserializowany obiekt produktu
|
||||
- Przechowuje tablicę danych produktu (z kombinacjami, obrazkami, producentem itd.)
|
||||
- TTL: 24 godziny (86400 sekund)
|
||||
- Klasa: `shop\Product::getFromCache()` - `autoload/shop/class.Product.php:121`
|
||||
- Klasa: `Domain\Product\ProductRepository::findCached()` - `autoload/Domain/Product/ProductRepository.php`
|
||||
|
||||
#### Opcje ilościowe produktu
|
||||
```
|
||||
\shop\Product::get_product_permutation_quantity_options:{product_id}:{permutation}
|
||||
ProductRepository::getProductPermutationQuantityOptions:v2:{product_id}:{permutation}
|
||||
```
|
||||
- Przechowuje informacje o ilości i komunikatach magazynowych
|
||||
- Klasa: `shop\Product::get_product_permutation_quantity_options()` - `autoload/shop/class.Product.php:549`
|
||||
- Klasa: `Domain\Product\ProductRepository::getProductPermutationQuantityOptions()` - `autoload/Domain/Product/ProductRepository.php`
|
||||
|
||||
#### Zestawy produktów
|
||||
```
|
||||
\shop\Product::product_sets_when_add_to_basket:{product_id}
|
||||
ProductRepository::productSetsWhenAddToBasket:{product_id}
|
||||
```
|
||||
- Przechowuje produkty często kupowane razem
|
||||
- Klasa: `shop\Product::product_sets_when_add_to_basket()` - `autoload/shop/class.Product.php:316`
|
||||
- Klasa: `Domain\Product\ProductRepository::productSetsWhenAddToBasket()` - `autoload/Domain/Product/ProductRepository.php`
|
||||
|
||||
## Integracje z systemami zewnętrznymi (CRON)
|
||||
|
||||
@@ -179,12 +179,12 @@ Główna klasa helper (przeniesiona z `class.S.php`) z metodami:
|
||||
- ~~`\front\factory\`~~ - USUNIĘTY — wszystkie fabryki zmigrowane do Domain
|
||||
- ~~`\front\controls\`~~ - USUNIĘTY — router przeniesiony do `\front\App`
|
||||
- ~~`\front\view\`~~ - USUNIĘTY — layout engine przeniesiony do `\front\LayoutEngine`
|
||||
- `\shop\` - klasy sklepu (Product, Order, itp.)
|
||||
- ~~`\shop\`~~ - USUNIĘTY — wszystkie klasy zmigrowane do `\Domain\`
|
||||
|
||||
### Cachowanie produktów
|
||||
```php
|
||||
// Pobranie produktu z cache
|
||||
$product = \shop\Product::getFromCache($product_id, $lang_id, $permutation_hash);
|
||||
$product = (new \Domain\Product\ProductRepository($mdb))->findCached($product_id, $lang_id, $permutation_hash);
|
||||
|
||||
// Czyszczenie cache produktu
|
||||
\Shared\Helpers\Helpers::clear_product_cache($product_id);
|
||||
@@ -264,9 +264,9 @@ autoload/
|
||||
- `admin\factory\ShopTransport` i `front\factory\ShopTransport` przepiete na repozytorium.
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.270):**
|
||||
- `shop\Order` zapisuje nieudane syncy Apilo (status/platnosc) do kolejki `temp/apilo-sync-queue.json`.
|
||||
- `cron.php` automatycznie ponawia zalegle syncy (`Order::process_apilo_sync_queue()`).
|
||||
- `shop\Order::set_as_paid()` wysyla mapowany typ platnosci Apilo (z mapowania metody platnosci), bez stalej wartosci `type`.
|
||||
- `OrderAdminService` zapisuje nieudane syncy Apilo (status/platnosc) do kolejki `temp/apilo-sync-queue.json`.
|
||||
- `cron.php` automatycznie ponawia zalegle syncy (`OrderAdminService::processApiloSyncQueue()`).
|
||||
- `OrderAdminService::setOrderAsPaid()` wysyla mapowany typ platnosci Apilo (z mapowania metody platnosci), bez stalej wartosci `type`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.276):**
|
||||
- Dodano modul domenowy `Domain/Order/OrderRepository.php`.
|
||||
|
||||
@@ -33,10 +33,17 @@ Alternatywnie (Git Bash):
|
||||
|
||||
## Aktualny stan suite
|
||||
|
||||
Ostatnio zweryfikowano: 2026-02-17
|
||||
Ostatnio zweryfikowano: 2026-02-18
|
||||
|
||||
```text
|
||||
OK (610 tests, 1816 assertions)
|
||||
OK (610 tests, 1817 assertions)
|
||||
```
|
||||
|
||||
Aktualizacja po usunieciu autoload/shop/ — 12 legacy klas (2026-02-18, ver. 0.294):
|
||||
```text
|
||||
Pelny suite: OK (610 tests, 1817 assertions)
|
||||
Zmodyfikowane testy: PromotionRepositoryTest (cache key \shop\Promotion → PromotionRepository)
|
||||
Zmodyfikowane testy: ShopOrderControllerTest (zamiana \shop\Coupon → CouponRepository DI)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji front\controls\Site + front\view\Site (2026-02-17, ver. 0.293):
|
||||
|
||||
@@ -18,16 +18,16 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
||||
|
||||
## Procedura tworzenia nowej aktualizacji
|
||||
|
||||
## Status biezacej aktualizacji (ver. 0.293)
|
||||
## Status biezacej aktualizacji (ver. 0.294)
|
||||
|
||||
- Wersja udostepniona: `0.293` (data: 2026-02-17).
|
||||
- Wersja udostepniona: `0.294` (data: 2026-02-18).
|
||||
- Pliki publikacyjne:
|
||||
- `temp/update_build/ver_0.293.zip`, `ver_0.293_files.txt`
|
||||
- `temp/update_build/ver_0.294.zip`, `ver_0.294_files.txt`
|
||||
- Pliki metadanych aktualizacji:
|
||||
- `updates/changelog.php` (dodany wpis `ver. 0.293`)
|
||||
- `updates/versions.php` (`$current_ver = 293`)
|
||||
- `updates/changelog.php` (dodany wpis `ver. 0.294`)
|
||||
- `updates/versions.php` (`$current_ver = 294`)
|
||||
- Weryfikacja testow przed publikacja:
|
||||
- `OK (610 tests, 1816 assertions)`
|
||||
- `OK (610 tests, 1817 assertions)`
|
||||
|
||||
### 1. Określ numer wersji
|
||||
Sprawdź ostatnią wersję w `updates/` i zwiększ o 1.
|
||||
|
||||
@@ -222,7 +222,7 @@ if ( $settings[ 'piksel' ] )
|
||||
"ViewCategory", {
|
||||
content_category: "kategoria",
|
||||
content_name: "' . htmlspecialchars( str_replace( '"', '', ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->categoryName( (int)$category['id'], $lang_id ) ) ) . '",
|
||||
content_ids: ["' . implode( ',', \shop\Category::get_category_products_id( $category['id'] ) ) . '"],
|
||||
content_ids: ["' . implode( ',', ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->getCategoryProductIds( (int)$category['id'] ) ) . '"],
|
||||
content_type: "product"
|
||||
});';
|
||||
|
||||
|
||||
@@ -3,29 +3,30 @@
|
||||
<h2>Dobierz inne produkty do kompletu</h2>
|
||||
<? endif;?>
|
||||
<div class="product-sets">
|
||||
<? $productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ); ?>
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
$product = new \shop\Product( $product_id );
|
||||
$product = $productRepo->findCached( $product_id, $lang_id );
|
||||
|
||||
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product -> language['name'] );
|
||||
$product['language']['seo_link'] ? $url = '/' . $product['language']['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
?>
|
||||
<div class="product">
|
||||
<? if ( $product -> new_to_date and $product -> new_to_date >= date( 'Y-m-d' ) ):?>
|
||||
<? if ( $product['new_to_date'] and $product['new_to_date'] >= date( 'Y-m-d' ) ):?>
|
||||
<div class="new"><?= \Shared\Helpers\Helpers::lang( 'nowosc' );?></div>
|
||||
<? endif;?>
|
||||
<a href="<?= $url;?>" class="a-img">
|
||||
<div class="img">
|
||||
<? if ( file_exists( substr( $product -> images[0]['src'], 1 ) ) ):?>
|
||||
<? if ( file_exists( substr( $product['images'][0]['src'], 1 ) ) ):?>
|
||||
<? if ( $this -> class == 'item' ):?>
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="lozad first" data-src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $product -> images[1]['src'], 1 ) ) ):?>
|
||||
<img class="lozad second" data-src="/thumb/490/0/<?= substr( $product -> images[1]['src'], 1, strlen( $product -> images[1]['src'] ) );?>" alt="<?= $product -> images[1]['alt'];?>">
|
||||
<img class="lozad first" data-src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $product['images'][1]['src'], 1 ) ) ):?>
|
||||
<img class="lozad second" data-src="/thumb/490/0/<?= substr( $product['images'][1]['src'], 1, strlen( $product['images'][1]['src'] ) );?>" alt="<?= $product['images'][1]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="lozad second" data-src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<img class="lozad second" data-src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? endif;?>
|
||||
<? endif;?>
|
||||
<? else:?>
|
||||
@@ -36,20 +37,20 @@
|
||||
<div class="product-details">
|
||||
<h2 class="name">
|
||||
<a href="<?= $url;?>" class="a-name">
|
||||
<?= $product -> language['name'];?>
|
||||
<?= $product['language']['name'];?>
|
||||
</a>
|
||||
</h2>
|
||||
<div class="prices">
|
||||
<? if ( $product -> price_brutto_promo ):?>
|
||||
<? if ( $product['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto_promo );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;?>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<? if ( $this -> custom_fields ) : ?>
|
||||
<? foreach ( $this -> custom_fields as $key => $val ) : ?>
|
||||
<? $custom_field = \shop\ProductCustomField::getFromCache( $key ); ?>
|
||||
<? $custom_field = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCustomFieldCached( $key ); ?>
|
||||
|
||||
<? if ( $custom_field['type'] == 'text' ) : ?>
|
||||
<div class="custom-field">
|
||||
|
||||
@@ -3,29 +3,30 @@
|
||||
<h2>Dobierz inne produkty do kompletu</h2>
|
||||
<? endif;?>
|
||||
<div class="product-sets">
|
||||
<? $productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ); ?>
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
$product = \shop\Product::getFromCache( $product_id, $lang_id );
|
||||
$product = $productRepo->findCached( $product_id, $lang_id );
|
||||
|
||||
$product -> language['seo_link'] ? $url = '/' . $product -> language['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product -> language['name'] );
|
||||
$product['language']['seo_link'] ? $url = '/' . $product['language']['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
?>
|
||||
<div class="product">
|
||||
<? if ( $product -> new_to_date and $product -> new_to_date >= date( 'Y-m-d' ) ):?>
|
||||
<? if ( $product['new_to_date'] and $product['new_to_date'] >= date( 'Y-m-d' ) ):?>
|
||||
<div class="new"><?= \Shared\Helpers\Helpers::lang( 'nowosc' );?></div>
|
||||
<? endif;?>
|
||||
<a href="<?= $url;?>" class="a-img">
|
||||
<div class="img">
|
||||
<? if ( file_exists( substr( $product -> images[0]['src'], 1 ) ) ):?>
|
||||
<? if ( file_exists( substr( $product['images'][0]['src'], 1 ) ) ):?>
|
||||
<? if ( $this -> class == 'item' ):?>
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="first" src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $product -> images[1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $product -> images[1]['src'], 1, strlen( $product -> images[1]['src'] ) );?>" alt="<?= $product -> images[1]['alt'];?>">
|
||||
<img class="first" src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $product['images'][1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $product['images'][1]['src'], 1, strlen( $product['images'][1]['src'] ) );?>" alt="<?= $product['images'][1]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $product -> images[0]['src'], 1, strlen( $product -> images[0]['src'] ) );?>" alt="<?= $product -> images[0]['alt'];?>">
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $product['images'][0]['src'], 1, strlen( $product['images'][0]['src'] ) );?>" alt="<?= $product['images'][0]['alt'];?>">
|
||||
<? endif;?>
|
||||
<? endif;?>
|
||||
<? else:?>
|
||||
@@ -36,39 +37,39 @@
|
||||
<div class="product-details">
|
||||
<h2 class="name">
|
||||
<a href="<?= $url;?>" class="a-name">
|
||||
<?= $product -> language['name'];?>
|
||||
<?= $product['language']['name'];?>
|
||||
</a>
|
||||
</h2>
|
||||
<div class="prices">
|
||||
<?
|
||||
$prices = $product -> getDefaultCombinationPrices();
|
||||
$prices = $productRepo->getDefaultCombinationPrices( $product );
|
||||
if ( $prices )
|
||||
{
|
||||
if ( $prices['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $product -> price_brutto_promo ):?>
|
||||
if ( $product['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto_promo );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
};?>
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<?
|
||||
unset( $price );
|
||||
unset( $price_new );
|
||||
$product = \shop\Product::getFromCache( (int)$position['product-id'], $this -> lang_id );
|
||||
$product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( (int)$position['product-id'], $this -> lang_id );
|
||||
|
||||
$permutation = null;
|
||||
if ( isset( $position['parent_id'] ) and (int)$position['parent_id'] and isset( $position['product-id'] ) )
|
||||
$permutation = \shop\Product::get_product_permutation_hash( (int)$position['product-id'] );
|
||||
$permutation = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->getProductPermutationHash( (int)$position['product-id'] );
|
||||
|
||||
if ( !$permutation and isset( $position['attributes'] ) and is_array( $position['attributes'] ) and count( $position['attributes'] ) )
|
||||
$permutation = implode( '|', $position['attributes'] );
|
||||
|
||||
$quantity_options = \shop\Product::get_product_permutation_quantity_options(
|
||||
$quantity_options = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->getProductPermutationQuantityOptions(
|
||||
(int)( $position['parent_id'] ? $position['parent_id'] : $position['product-id'] ),
|
||||
$permutation
|
||||
);
|
||||
@@ -63,7 +63,7 @@
|
||||
<?= \Shared\Tpl\Tpl::view( 'shop-basket/_partials/product-custom-fields', [
|
||||
'custom_fields' => $position['custom_fields']
|
||||
] ); ?>
|
||||
<? if ( $product -> additional_message ):?>
|
||||
<? if ( $product['additional_message'] ):?>
|
||||
<div class="basket-product-message">
|
||||
<textarea name="product-message" class="form-control" position="<?= $position_hash;?>" placeholder="Miejsce na dodatkową wiadomość"><?= htmlspecialchars( $position['message'] );?></textarea>
|
||||
</div>
|
||||
@@ -72,7 +72,7 @@
|
||||
<div class="prices">
|
||||
<div class="price">
|
||||
<?
|
||||
$price_product = \shop\Product::calculate_basket_product_price( (float)$product['price_brutto_promo'], (float)$product['price_brutto'], $this -> coupon, $position );
|
||||
$price_product = \Domain\Basket\BasketCalculator::calculateBasketProductPrice( (float)$product['price_brutto_promo'], (float)$product['price_brutto'], $this -> coupon, $position );
|
||||
|
||||
if ( $price_product['price_new'] )
|
||||
echo \Shared\Helpers\Helpers::decimal( $price_product['price_new'] ) . ' zł';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div id="basket-summary" class="content">
|
||||
<? if ( is_array( $this -> basket ) and count( $this -> basket ) ):?>
|
||||
<? foreach ( $this -> basket as $position_hash => $position ):?>
|
||||
<? $product = \shop\Product::getFromCache( $position[ 'product-id' ], $this -> lang_id );?>
|
||||
<? $product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $position[ 'product-id' ], $this -> lang_id );?>
|
||||
<div class="basket-product">
|
||||
<div class="image">
|
||||
<? if ( file_exists( substr( $product[ 'images' ][ 0 ][ 'src' ], 1 ) ) ):?>
|
||||
@@ -51,7 +51,7 @@
|
||||
<div class="prices">
|
||||
<div class="price">
|
||||
<?
|
||||
$price_product = \shop\Product::calculate_basket_product_price((float)$product['price_brutto_promo'], (float)$product['price_brutto'], $this -> coupon, $position);
|
||||
$price_product = \Domain\Basket\BasketCalculator::calculateBasketProductPrice((float)$product['price_brutto_promo'], (float)$product['price_brutto'], $this -> coupon, $position);
|
||||
|
||||
if ($price_product['price_new'])
|
||||
echo \Shared\Helpers\Helpers::decimal($price_product['price_new']) . ' zł';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="row">
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):?>
|
||||
<?= \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( $product_id, $lang_id ),
|
||||
'product' => (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id ),
|
||||
'class' => 'col-12 col-sm-6 col-lg-3'
|
||||
] );?>
|
||||
<? endforeach; endif;?>
|
||||
|
||||
@@ -14,7 +14,7 @@ endif;
|
||||
<div class="category-content">
|
||||
<? if (is_array($this->products)) : foreach ($this->products as $product_id) :
|
||||
echo \Shared\Tpl\Tpl::view('shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( $product_id, $lang_id )
|
||||
'product' => (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id )
|
||||
]);
|
||||
endforeach;
|
||||
endif; ?>
|
||||
|
||||
@@ -16,7 +16,7 @@ endif;
|
||||
|
||||
if ( $this -> category['view_subcategories'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-category/_partials/category-subcategory-tiles', [
|
||||
'categories' => \shop\Category::get_subcategory_by_category( $this -> category['id'] ),
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategoriesLangCached( (int)$this -> category['id'] ),
|
||||
] );
|
||||
|
||||
endif;
|
||||
@@ -25,7 +25,7 @@ endif;
|
||||
<div class="category-content" t='2'>
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( $product_id, $lang_id )
|
||||
'product' => (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id )
|
||||
] );
|
||||
endforeach; endif;?>
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
</div>
|
||||
<? endif;?>
|
||||
<div class="category-content">
|
||||
<? $productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ); ?>
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
$product = new \shop\Product( $product_id );
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => new \shop\Product( $product_id, $lang_id )
|
||||
'product' => $productRepo->findCached( $product_id, $lang_id )
|
||||
] );
|
||||
endforeach; endif;?>
|
||||
</div>
|
||||
@@ -9,7 +9,7 @@
|
||||
<? if ( is_array( $this -> orders ) ): foreach ( $this -> orders as $order ):?>
|
||||
<?= \Shared\Tpl\Tpl::view( 'shop-order/order-simple', [
|
||||
'order' => $order,
|
||||
'statuses' => \shop\Order::order_statuses()
|
||||
'statuses' => ( new \Domain\Order\OrderRepository( $GLOBALS['mdb'] ) )->orderStatuses()
|
||||
] );?>
|
||||
<? endforeach; endif;?>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@ echo $this -> settings['newsletter_header'];
|
||||
</thead>
|
||||
<tbody>
|
||||
<? if ( is_array( $this -> order['products'] ) ): foreach ( $this -> order['products'] as $position ):?>
|
||||
<? $product = \shop\Product::getFromCache( $position['product_id'], $lang_id );?>
|
||||
<? $product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $position['product_id'], $lang_id );?>
|
||||
<tr style="border-bottom: 1px solid #eee;">
|
||||
<td style="padding: 10px 10px 10px 0;" colspan="4">
|
||||
<?
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<?= \Shared\Tpl\Tpl::view( 'shop-order/order-simple', [
|
||||
'order' => $this -> order,
|
||||
'coupon' => $this -> coupon,
|
||||
'statuses' => \shop\Order::order_statuses()
|
||||
'statuses' => ( new \Domain\Order\OrderRepository( $GLOBALS['mdb'] ) )->orderStatuses()
|
||||
] );?>
|
||||
<? if ( $this -> order['status'] == 0 or $this -> order['status'] == 2 ):?>
|
||||
<div class="order-bottom">
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="products">
|
||||
<? if ( is_array( $this -> order['products'] ) and count( $this -> order['products'] ) ):?>
|
||||
<? foreach ( $this -> order['products'] as $product ):?>
|
||||
<? $product_tmp = \shop\Product::getFromCache( $product['product_id'], $lang['id'] );?>
|
||||
<? $product_tmp = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product['product_id'], $lang['id'] );?>
|
||||
<div class="basket-product">
|
||||
<div class="image">
|
||||
<? if ( file_exists( substr( $product_tmp['images'][0]['src'], 1 ) ) ):?>
|
||||
@@ -90,7 +90,7 @@
|
||||
<? endif;?>
|
||||
<? if ( $this -> coupon ):?>
|
||||
<div class="basket-summary">
|
||||
Kod rabatowy: <span style="color: #cc0000;"><?= $this -> coupon -> name;?> - <?= $this -> coupon -> amount;?> <?= $this -> coupon -> type == 1 ? '%' : 'zł';?></span>
|
||||
Kod rabatowy: <span style="color: #cc0000;"><?= $this -> coupon['name'];?> - <?= $this -> coupon['amount'];?> <?= $this -> coupon['type'] == 1 ? '%' : 'zł';?></span>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<div class="basket-summary">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="category-content">
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product = \shop\Product::getFromCache( $product_id, $lang_id )
|
||||
'product' => $product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id )
|
||||
] );
|
||||
endforeach; endif;?>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ if ( $attribute_details['type'] == 0 )
|
||||
<? foreach ( $this -> attribute['values'] as $value ):?>
|
||||
<div class="fradio">
|
||||
<input type="radio" id="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" <? if ( $value['is_default'] ):?>checked="checked"<? endif;?> require="true" value="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" name="<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>">
|
||||
<label for="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" order="<?= $this -> order;?>"><?= \shop\ProductAttribute::get_value_name( $value['id'], $lang_id );?></label>
|
||||
<label for="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" order="<?= $this -> order;?>"><?= ( new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] ) )->getAttributeValueById( $value['id'], $lang_id );?></label>
|
||||
</div>
|
||||
<? if ( $value['is_default'] ):?>
|
||||
<script class="footer" type="text/javascript">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
use shop\Product;
|
||||
$product_meta = Product::product_meta($this->product_id);
|
||||
$product_meta = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->productMeta( $this->product_id );
|
||||
?>
|
||||
|
||||
<?php if (is_array($product_meta) && count($product_meta)): ?>
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
{
|
||||
if ( $this -> product['quantity'] > 0 or $this -> product['quantity'] == 0 and $this -> product['stock_0_buy'] )
|
||||
{
|
||||
if ( $this -> product -> language['warehouse_message_nonzero'] )
|
||||
echo $this -> product -> language['warehouse_message_nonzero'];
|
||||
if ( $this -> product['language']['warehouse_message_nonzero'] )
|
||||
echo $this -> product['language']['warehouse_message_nonzero'];
|
||||
else if ( $this -> settings['warehouse_message_nonzero_' . $this -> lang_id] )
|
||||
echo $this -> settings['warehouse_message_nonzero_' . $this -> lang_id];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $this -> product -> language['warehouse_message_zero'] )
|
||||
echo $this -> product -> language['warehouse_message_zero'];
|
||||
if ( $this -> product['language']['warehouse_message_zero'] )
|
||||
echo $this -> product['language']['warehouse_message_zero'];
|
||||
else if ( $this -> settings['warehouse_message_zero_' . $this -> lang_id] )
|
||||
echo $this -> settings['warehouse_message_zero_' . $this -> lang_id];
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<? global $lang_id;?>
|
||||
<?
|
||||
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \Shared\Helpers\Helpers::seo( $this -> product -> language['name'] );
|
||||
$this -> product['language']['seo_link'] ? $url = '/' . $this -> product['language']['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \Shared\Helpers\Helpers::seo( $this -> product['language']['name'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
?>
|
||||
<div class="product-mini <?= $this -> product_mini_class;?>">
|
||||
<? if ( $this -> product -> new_to_date and $this -> product -> new_to_date >= date( 'Y-m-d' ) ):?>
|
||||
<? if ( $this -> product['new_to_date'] and $this -> product['new_to_date'] >= date( 'Y-m-d' ) ):?>
|
||||
<div class="new"><?= \Shared\Helpers\Helpers::lang( 'nowosc' );?></div>
|
||||
<? endif;?>
|
||||
<a href="<?= $url;?>">
|
||||
<div class="img">
|
||||
<? if ( file_exists( substr( $this -> product -> images[0]['src'], 1 ) ) ):?>
|
||||
<? if ( file_exists( substr( $this -> product['images'][0]['src'], 1 ) ) ):?>
|
||||
<? if ( $this -> class == 'item' ):?>
|
||||
<img class="<?= $this -> product_mini_class == 'swiper-slide' ? '' : 'owl-lazy';?>" src="/thumb/500/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<img class="<?= $this -> product_mini_class == 'swiper-slide' ? '' : 'owl-lazy';?>" src="/thumb/500/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="first" src="/thumb/500/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $this -> product -> images[1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/1000/0/<?= substr( $this -> product -> images[1]['src'], 1, strlen( $this -> product -> images[1]['src'] ) );?>" alt="<?= $this -> product -> images[1]['alt'];?>">
|
||||
<img class="first" src="/thumb/500/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $this -> product['images'][1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/1000/0/<?= substr( $this -> product['images'][1]['src'], 1, strlen( $this -> product['images'][1]['src'] ) );?>" alt="<?= $this -> product['images'][1]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="second" src="/thumb/1000/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<img class="second" src="/thumb/1000/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? endif;?>
|
||||
<? endif;?>
|
||||
<? else:?>
|
||||
@@ -28,38 +28,38 @@
|
||||
</div>
|
||||
<div class="product-details">
|
||||
<h2 class="name">
|
||||
<?= $this -> product -> language['name'];?>
|
||||
<?= $this -> product['language']['name'];?>
|
||||
</h2>
|
||||
<div class="prices">
|
||||
<?
|
||||
$prices = $this -> product -> getDefaultCombinationPrices();
|
||||
$prices = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getDefaultCombinationPrices( $this -> product );
|
||||
if ( $prices )
|
||||
{
|
||||
if ( $prices['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $this -> product -> price_brutto_promo ):?>
|
||||
if ( $this -> product['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto_promo );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
};?>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<? global $lang_id, $mdb;?>
|
||||
<?
|
||||
$attributes = \shop\Product::get_product_attributes( $this -> product['product_combinations'] );
|
||||
$attributes = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->getProductAttributes( $this -> product['product_combinations'] );
|
||||
$dictionariesRepository = new \Domain\Dictionaries\DictionariesRepository( $mdb );
|
||||
?>
|
||||
<script class="footer" type="text/javascript">
|
||||
@@ -19,21 +19,21 @@
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div id="image-big">
|
||||
<? if ( $this -> product -> new_to_date and $this -> product -> new_to_date >= date( 'Y-m-d' ) ):?>
|
||||
<? if ( $this -> product['new_to_date'] and $this -> product['new_to_date'] >= date( 'Y-m-d' ) ):?>
|
||||
<div class="new"><?= \Shared\Helpers\Helpers::lang( 'nowosc' );?></div>
|
||||
<? endif;?>
|
||||
<? if ( file_exists( substr( $this -> product -> images[0]['src'], 1 ) ) ):?>
|
||||
<? if ( file_exists( substr( $this -> product['images'][0]['src'], 1 ) ) ):?>
|
||||
<a image-id="0">
|
||||
<img src="/thumb/625/0<?= $this -> product -> images[0]['src'];?>" alt="">
|
||||
<img src="/thumb/625/0<?= $this -> product['images'][0]['src'];?>" alt="">
|
||||
</a>
|
||||
<? else:?>
|
||||
|
||||
<? endif;?>
|
||||
</div>
|
||||
<div class="gallery">
|
||||
<? for ( $i = 0; $i < count( $this -> product -> images ); $i++ ): if ( file_exists( substr( $this -> product -> images[$i]['src'], 1 ) ) ):?>
|
||||
<a href="<?= $this -> product -> images[$i]['src'];?>" class="item" data-fancybox="product" id="image-<?= $i;?>" i="<?= $i;?>">
|
||||
<img src="/thumb/180/0<?= $this -> product -> images[$i]['src'];?>" src="" alt="<?= $this -> product -> images[$i]['alt'];?>">
|
||||
<? for ( $i = 0; $i < count( $this -> product['images'] ); $i++ ): if ( file_exists( substr( $this -> product['images'][$i]['src'], 1 ) ) ):?>
|
||||
<a href="<?= $this -> product['images'][$i]['src'];?>" class="item" data-fancybox="product" id="image-<?= $i;?>" i="<?= $i;?>">
|
||||
<img src="/thumb/180/0<?= $this -> product['images'][$i]['src'];?>" src="" alt="<?= $this -> product['images'][$i]['alt'];?>">
|
||||
</a>
|
||||
<? endif; endfor;?>
|
||||
</div>
|
||||
@@ -45,36 +45,36 @@
|
||||
'lang_id' => $this -> lang_id,
|
||||
'settings' => $this -> settings
|
||||
] );?>
|
||||
<h1><?= $this -> product -> language['name'];?></h1>
|
||||
<h1><?= $this -> product['language']['name'];?></h1>
|
||||
<div class="prices">
|
||||
<? if ( $this -> product -> price_brutto_promo ):?>
|
||||
<? if ( $this -> product['price_brutto_promo'] ):?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto_promo );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price-minimal">
|
||||
Najniższa cena w ciągu ostatnich 30 dni: <strong><?= \shop\Shop::shortPrice( ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getMinimalPriceCached( (int)$this -> product['id'], $this -> product -> price_brutto_promo ) );?> zł</strong>
|
||||
Najniższa cena w ciągu ostatnich 30 dni: <strong><?= \Shared\Helpers\Helpers::shortPrice( ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getMinimalPriceCached( (int)$this -> product['id'], $this -> product['price_brutto_promo'] ) );?> zł</strong>
|
||||
</div>
|
||||
<? if ( $this -> product -> weight > 0 && $this -> product -> product_unit_id ):?>
|
||||
<? if ( $this -> product['weight'] > 0 && $this -> product['product_unit_id'] ):?>
|
||||
<div class="price_weight">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto_promo / $this -> product -> wegiht * 1 );?> <?= $this -> product -> product_unit;?>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto_promo'] / $this -> product['wegiht'] * 1 );?> <?= $this -> product['product_unit'];?>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? if ( $this -> product -> weight > 0 && $this -> product -> product_unit_id ):?>
|
||||
<? if ( $this -> product['weight'] > 0 && $this -> product['product_unit_id'] ):?>
|
||||
<div class="price_weight">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto / $this -> product -> weight );?> <span class="small">zł</span> / <?= $dictionariesRepository -> getUnitNameById( (int)$this -> product -> product_unit_id, $this -> lang_id );?>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] / $this -> product['weight'] );?> <span class="small">zł</span> / <?= $dictionariesRepository -> getUnitNameById( (int)$this -> product['product_unit_id'], $this -> lang_id );?>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<? endif;?>
|
||||
</div>
|
||||
<form class="product-options" id="product-<?= $this -> product['id'];?>">
|
||||
<input type="hidden" name="product-id" value="<?= $this -> product -> id;?>">
|
||||
<input type="hidden" name="product-id" value="<?= $this -> product['id'];?>">
|
||||
<? if ( \Shared\Helpers\Helpers::is_array_fix( $attributes ) ):?>
|
||||
<div id="attributes">
|
||||
<div class="title">Opcje produktu</div>
|
||||
@@ -114,8 +114,8 @@
|
||||
<script class="footer" type="text/javascript">
|
||||
$(function ()
|
||||
{
|
||||
$('#product-<?= $this -> product -> id;?> .add-to-basket .quantity-container, #product-<?= $this -> product -> id;?> .add-to-basket #add-to-basket').addClass('hidden');
|
||||
$('#product-<?= $this -> product -> id;?> .add-to-basket .alert').removeClass('hidden');
|
||||
$('#product-<?= $this -> product['id'];?> .add-to-basket .quantity-container, #product-<?= $this -> product['id'];?> .add-to-basket #add-to-basket').addClass('hidden');
|
||||
$('#product-<?= $this -> product['id'];?> .add-to-basket .alert').removeClass('hidden');
|
||||
});
|
||||
</script>
|
||||
<? endif;?>
|
||||
@@ -132,7 +132,7 @@
|
||||
]);?>
|
||||
|
||||
<div class="short-description">
|
||||
<?= $this -> product -> language['short_description'];?>
|
||||
<?= $this -> product['language']['short_description'];?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,20 +142,20 @@
|
||||
<div class="col-12 col-lg-8">
|
||||
<ul class="tabs" id="tabs-menu">
|
||||
<li class="tab-link current" id="tab-link-0" tab="0"><?= ucfirst( \Shared\Helpers\Helpers::lang( 'opis' ) );?></li>
|
||||
<? if ( $this -> product -> language['tab_name_1'] ):?>
|
||||
<li class="tab-link" id="tab-link-1" tab="1"><?= $this -> product -> language['tab_name_1'];?></li>
|
||||
<? if ( $this -> product['language']['tab_name_1'] ):?>
|
||||
<li class="tab-link" id="tab-link-1" tab="1"><?= $this -> product['language']['tab_name_1'];?></li>
|
||||
<? endif;?>
|
||||
<li class="tab-link" id="tab-link-2" tab="2"><?= ucfirst( \Shared\Helpers\Helpers::lang( 'opinie' ) );?></li>
|
||||
<li class="tab-link" id="tab-link-3" tab="3">Bezpieczeństwo</li>
|
||||
</ul>
|
||||
<div id="tab-0" class="tab-content">
|
||||
<div class="tab-title"><?= \Shared\Helpers\Helpers::lang( 'opis-produktu' );?></div>
|
||||
<?= $this -> product -> language['description'];?>
|
||||
<?= $this -> product['language']['description'];?>
|
||||
</div>
|
||||
<? if ( $this -> product -> language['tab_description_1'] ):?>
|
||||
<? if ( $this -> product['language']['tab_description_1'] ):?>
|
||||
<div id="tab-1" class="tab-content">
|
||||
<div class="tab-title"><?= $this -> product -> language['tab_name_1'];?></div>
|
||||
<?= $this -> product -> language['tab_description_1'];?>
|
||||
<div class="tab-title"><?= $this -> product['language']['tab_name_1'];?></div>
|
||||
<?= $this -> product['language']['tab_description_1'];?>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<div id="tab-2" class="tab-content reviews">
|
||||
@@ -202,11 +202,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-4">
|
||||
<? if ( $this -> product -> language['tab_name_2'] ):?>
|
||||
<? if ( $this -> product['language']['tab_name_2'] ):?>
|
||||
<div class="description-right">
|
||||
<div class="title"><?= $this -> product -> language['tab_name_2'];?></div>
|
||||
<div class="title"><?= $this -> product['language']['tab_name_2'];?></div>
|
||||
<div class="content">
|
||||
<?= $this -> product -> language['tab_description_2'];?>
|
||||
<?= $this -> product['language']['tab_description_2'];?>
|
||||
</div>
|
||||
</div>
|
||||
<? endif;?>
|
||||
@@ -221,8 +221,8 @@
|
||||
<div class="owl-carousel owl-theme">
|
||||
<?
|
||||
foreach ( $this -> product['products_sets'] as $product_sets_id ):
|
||||
$product = \shop\Product::getFromCache( $product_sets_id, $lang_id );
|
||||
if ( $product and $product -> status ):
|
||||
$product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_sets_id, $lang_id );
|
||||
if ( $product and $product['status'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product,
|
||||
'class' => 'item'
|
||||
@@ -243,8 +243,8 @@
|
||||
<div class="owl-carousel owl-theme">
|
||||
<?
|
||||
foreach ( $this -> product['products_related'] as $product_related_id ):
|
||||
$product = \shop\Product::getFromCache( $product_related_id, $lang_id );
|
||||
if ( $product and $product -> status ):
|
||||
$product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_related_id, $lang_id );
|
||||
if ( $product and $product['status'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product,
|
||||
'class' => 'item'
|
||||
@@ -275,9 +275,9 @@
|
||||
ecommerce: {
|
||||
items: [
|
||||
{
|
||||
item_id: "<?= $this -> product -> id;?>",
|
||||
item_name: "<?= str_replace( '"', '', $this -> product -> language['name'] );?>",
|
||||
price: '<? if ( $this -> product -> price_brutto_promo ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto_promo ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto ); endif;?>',
|
||||
item_id: "<?= $this -> product['id'];?>",
|
||||
item_name: "<?= str_replace( '"', '', $this -> product['language']['name'] );?>",
|
||||
price: '<? if ( $this -> product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>',
|
||||
quantity: 1
|
||||
}
|
||||
]
|
||||
@@ -556,8 +556,8 @@
|
||||
if (!check)
|
||||
return false;
|
||||
|
||||
var values = $( '#product-<?= $this -> product -> id;?>' ).serializeArray();
|
||||
var quantity = $('#product-<?= $this -> product -> id;?> #quantity').val();
|
||||
var values = $( '#product-<?= $this -> product['id'];?>' ).serializeArray();
|
||||
var quantity = $('#product-<?= $this -> product['id'];?> #quantity').val();
|
||||
|
||||
jQuery.ajax(
|
||||
{
|
||||
@@ -586,15 +586,15 @@
|
||||
for (var i = 1; i <= quantity; i++)
|
||||
{
|
||||
fbq('track', 'AddToCart', {
|
||||
content_name: '<?= str_replace( '"', '', $this -> product -> language['name'] );?>',
|
||||
content_name: '<?= str_replace( '"', '', $this -> product['language']['name'] );?>',
|
||||
content_category: 'produkt',
|
||||
content_ids: ['<?= $this -> product -> id;?>'],
|
||||
content_ids: ['<?= $this -> product['id'];?>'],
|
||||
content_type: 'product',
|
||||
value: '<?
|
||||
if ( $this -> product -> price_brutto_promo ):
|
||||
echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto_promo );
|
||||
if ( $this -> product['price_brutto_promo'] ):
|
||||
echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] );
|
||||
else:
|
||||
echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto );
|
||||
echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] );
|
||||
endif;
|
||||
?>',
|
||||
currency: 'PLN'
|
||||
@@ -603,7 +603,7 @@
|
||||
<? endif;?>
|
||||
|
||||
<? if ( $this -> settings['google_tag_manager_id'] ):?>
|
||||
var add_to_cart_value = <? if ( $this -> product -> price_brutto_promo ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto_promo ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto ); endif;?> * quantity;
|
||||
var add_to_cart_value = <? if ( $this -> product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?> * quantity;
|
||||
dataLayer.push({ ecommerce: null });
|
||||
dataLayer.push({
|
||||
event: "add_to_cart",
|
||||
@@ -612,9 +612,9 @@
|
||||
value: add_to_cart_value,
|
||||
items: [
|
||||
{
|
||||
item_id: "<?= $this -> product -> id;?>",
|
||||
item_name: "<?= str_replace( '"', '', $this -> product -> language['name'] );?>",
|
||||
price: <? if ( $this -> product -> price_brutto_promo ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto_promo ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product -> price_brutto ); endif;?>,
|
||||
item_id: "<?= $this -> product['id'];?>",
|
||||
item_name: "<?= str_replace( '"', '', $this -> product['language']['name'] );?>",
|
||||
price: <? if ( $this -> product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>,
|
||||
quantity: quantity
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="swiper-wrapper">
|
||||
<?
|
||||
foreach ( $this -> products as $product ):
|
||||
if ( $product -> status ):
|
||||
if ( $product['status'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product,
|
||||
'class' => 'item',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="swiper-wrapper">
|
||||
<?
|
||||
foreach ( $this -> products as $product ):
|
||||
if ( $product -> status ):
|
||||
if ( $product['status'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product,
|
||||
'class' => 'item',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="swiper-wrapper">
|
||||
<?
|
||||
foreach ( $this -> products as $product ):
|
||||
if ( $product -> status ):
|
||||
if ( $product['status'] ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => $product,
|
||||
'class' => 'item',
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<div class="content">
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):?>
|
||||
<?
|
||||
$product = \shop\Product::getFromCache( $product_id, $lang_id );
|
||||
$product = (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id );
|
||||
|
||||
$product -> languages[$lang_id]['seo_link'] ? $url = '/' . $product -> languages[$lang_id]['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product -> languages[$lang_id]['name'] );
|
||||
$product['languages'][$lang_id]['seo_link'] ? $url = '/' . $product['languages'][$lang_id]['seo_link'] : $url = '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['languages'][$lang_id]['name'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<? global $lang_id;?>
|
||||
<?
|
||||
$this -> product -> language['seo_link'] ? $url = '/' . $this -> product -> language['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \Shared\Helpers\Helpers::seo( $this -> product -> language['name'] );
|
||||
$this -> product['language']['seo_link'] ? $url = '/' . $this -> product['language']['seo_link'] : $url = '/p-' . $this -> product['id'] . '-' . \Shared\Helpers\Helpers::seo( $this -> product['language']['name'] );
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get_session( 'current-lang' ) != ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->defaultLanguage() and $url != '#' )
|
||||
$url = '/' . \Shared\Helpers\Helpers::get_session( 'current-lang' ) . $url;
|
||||
?>
|
||||
<div class="product-search">
|
||||
<? if ( $this -> product -> new_to_date and $this -> product -> new_to_date >= date( 'Y-m-d' ) ):?>
|
||||
<? if ( $this -> product['new_to_date'] and $this -> product['new_to_date'] >= date( 'Y-m-d' ) ):?>
|
||||
<div class="new"><?= \Shared\Helpers\Helpers::lang( 'nowosc' );?></div>
|
||||
<? endif;?>
|
||||
<a href="<?= $url;?>" class="a-img">
|
||||
<div class="img">
|
||||
<? if ( file_exists( substr( $this -> product -> images[0]['src'], 1 ) ) ):?>
|
||||
<? if ( file_exists( substr( $this -> product['images'][0]['src'], 1 ) ) ):?>
|
||||
<? if ( $this -> class == 'item' ):?>
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<img class="owl-lazy" data-src="/thumb/490/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="first" src="/thumb/490/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $this -> product -> images[1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $this -> product -> images[1]['src'], 1, strlen( $this -> product -> images[1]['src'] ) );?>" alt="<?= $this -> product -> images[1]['alt'];?>">
|
||||
<img class="first" src="/thumb/490/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? if ( file_exists( substr( $this -> product['images'][1]['src'], 1 ) ) ):?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $this -> product['images'][1]['src'], 1, strlen( $this -> product['images'][1]['src'] ) );?>" alt="<?= $this -> product['images'][1]['alt'];?>">
|
||||
<? else:?>
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $this -> product -> images[0]['src'], 1, strlen( $this -> product -> images[0]['src'] ) );?>" alt="<?= $this -> product -> images[0]['alt'];?>">
|
||||
<img class="second" src="/thumb/490/0/<?= substr( $this -> product['images'][0]['src'], 1, strlen( $this -> product['images'][0]['src'] ) );?>" alt="<?= $this -> product['images'][0]['alt'];?>">
|
||||
<? endif;?>
|
||||
<? endif;?>
|
||||
<? else:?>
|
||||
@@ -30,39 +30,39 @@
|
||||
<div class="product-details">
|
||||
<h2 class="name">
|
||||
<a href="<?= $url;?>" class="a-name">
|
||||
<?= $this -> product -> language['name'];?>
|
||||
<?= $this -> product['language']['name'];?>
|
||||
</a>
|
||||
</h2>
|
||||
<div class="prices">
|
||||
<?
|
||||
$prices = $this -> product -> getDefaultCombinationPrices();
|
||||
$prices = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->getDefaultCombinationPrices( $this -> product );
|
||||
if ( $prices )
|
||||
{
|
||||
if ( $prices['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $prices['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $this -> product -> price_brutto_promo ):?>
|
||||
if ( $this -> product['price_brutto_promo'] ):?>
|
||||
<div class="price-old">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto_promo );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto_promo'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? else:?>
|
||||
<div class="price">
|
||||
<?= \shop\Shop::shortPrice( $this -> product -> price_brutto );?> <span class="small">zł</span>
|
||||
<?= \Shared\Helpers\Helpers::shortPrice( $this -> product['price_brutto'] );?> <span class="small">zł</span>
|
||||
</div>
|
||||
<? endif;
|
||||
};?>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="search-content">
|
||||
<? if ( is_array( $this -> products ) ): foreach ( $this -> products as $product_id ):
|
||||
echo \Shared\Tpl\Tpl::view( 'shop-product/product-mini', [
|
||||
'product' => \shop\Product::getFromCache( $product_id, $lang_id )
|
||||
'product' => (new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached( $product_id, $lang_id )
|
||||
] );
|
||||
endforeach; endif;?>
|
||||
</div>
|
||||
@@ -181,15 +181,6 @@ class PromotionRepositoryTest extends TestCase
|
||||
// Frontend: basket promotion logic (migrated from front\factory\ShopPromotion)
|
||||
// =========================================================================
|
||||
|
||||
private function mockPromotion(array $data): object
|
||||
{
|
||||
return new class($data) {
|
||||
private $data;
|
||||
public function __construct($data) { $this->data = $data; }
|
||||
public function __get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; }
|
||||
};
|
||||
}
|
||||
|
||||
private function makeBasket(array $items): array
|
||||
{
|
||||
$basket = [];
|
||||
@@ -212,12 +203,12 @@ class PromotionRepositoryTest extends TestCase
|
||||
$mockStmt->method('fetchAll')->willReturn([['category_id' => 1]]);
|
||||
$mockDb->method('query')->willReturn($mockStmt);
|
||||
|
||||
$promotion = $this->mockPromotion([
|
||||
$promotion = [
|
||||
'discount_type' => 1,
|
||||
'amount' => 10,
|
||||
'include_coupon' => 0,
|
||||
'include_product_promo' => 0,
|
||||
]);
|
||||
];
|
||||
|
||||
$basket = $this->makeBasket([
|
||||
['id' => 1],
|
||||
@@ -244,14 +235,14 @@ class PromotionRepositoryTest extends TestCase
|
||||
$mockStmt->method('fetchAll')->willReturn([['category_id' => 5]]);
|
||||
$mockDb->method('query')->willReturn($mockStmt);
|
||||
|
||||
$promotion = $this->mockPromotion([
|
||||
'categories' => json_encode([5]),
|
||||
'condition_categories' => json_encode([10]),
|
||||
$promotion = [
|
||||
'categories' => [5],
|
||||
'condition_categories' => [10],
|
||||
'discount_type' => 1,
|
||||
'amount' => 15,
|
||||
'include_coupon' => 1,
|
||||
'include_product_promo' => 0,
|
||||
]);
|
||||
];
|
||||
|
||||
$basket = $this->makeBasket([['id' => 1]]);
|
||||
|
||||
@@ -282,14 +273,14 @@ class PromotionRepositoryTest extends TestCase
|
||||
);
|
||||
$mockDb->method('query')->willReturn($mockStmt1);
|
||||
|
||||
$promotion = $this->mockPromotion([
|
||||
'categories' => json_encode([5]),
|
||||
'condition_categories' => json_encode([10]),
|
||||
$promotion = [
|
||||
'categories' => [5],
|
||||
'condition_categories' => [10],
|
||||
'discount_type' => 1,
|
||||
'amount' => 20,
|
||||
'include_coupon' => 0,
|
||||
'include_product_promo' => 0,
|
||||
]);
|
||||
];
|
||||
|
||||
$basket = $this->makeBasket([
|
||||
['id' => 1],
|
||||
@@ -316,14 +307,14 @@ class PromotionRepositoryTest extends TestCase
|
||||
$mockStmt->method('fetchAll')->willReturn([['category_id' => 99]]); // nie pasuje do condition_categories
|
||||
$mockDb->method('query')->willReturn($mockStmt);
|
||||
|
||||
$promotion = $this->mockPromotion([
|
||||
'categories' => json_encode([5]),
|
||||
'condition_categories' => json_encode([10]),
|
||||
$promotion = [
|
||||
'categories' => [5],
|
||||
'condition_categories' => [10],
|
||||
'discount_type' => 1,
|
||||
'amount' => 20,
|
||||
'include_coupon' => 0,
|
||||
'include_product_promo' => 0,
|
||||
]);
|
||||
];
|
||||
|
||||
$basket = $this->makeBasket([['id' => 1]]);
|
||||
|
||||
@@ -353,14 +344,14 @@ class PromotionRepositoryTest extends TestCase
|
||||
);
|
||||
$mockDb->method('query')->willReturn($mockStmt);
|
||||
|
||||
$promotion = $this->mockPromotion([
|
||||
'categories' => json_encode([5]),
|
||||
'condition_categories' => json_encode([10]),
|
||||
$promotion = [
|
||||
'categories' => [5],
|
||||
'condition_categories' => [10],
|
||||
'discount_type' => 1,
|
||||
'amount' => 25,
|
||||
'include_coupon' => 1,
|
||||
'include_product_promo' => 0,
|
||||
]);
|
||||
];
|
||||
|
||||
$basket = $this->makeBasket([
|
||||
['id' => 1],
|
||||
|
||||
@@ -4,21 +4,24 @@ namespace Tests\Unit\front\Controllers;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use front\Controllers\ShopOrderController;
|
||||
use Domain\Order\OrderRepository;
|
||||
use Domain\Order\OrderAdminService;
|
||||
|
||||
class ShopOrderControllerTest extends TestCase
|
||||
{
|
||||
private $repository;
|
||||
private $adminService;
|
||||
private $controller;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->createMock(OrderRepository::class);
|
||||
$this->controller = new ShopOrderController($this->repository);
|
||||
$this->adminService = $this->createMock(OrderAdminService::class);
|
||||
$this->controller = new ShopOrderController($this->repository, $this->adminService);
|
||||
}
|
||||
|
||||
public function testConstructorAcceptsRepository(): void
|
||||
{
|
||||
$controller = new ShopOrderController($this->repository);
|
||||
$controller = new ShopOrderController($this->repository, $this->adminService);
|
||||
$this->assertInstanceOf(ShopOrderController::class, $controller);
|
||||
}
|
||||
|
||||
@@ -37,7 +40,8 @@ class ShopOrderControllerTest extends TestCase
|
||||
$constructor = $reflection->getConstructor();
|
||||
$params = $constructor->getParameters();
|
||||
|
||||
$this->assertCount(1, $params);
|
||||
$this->assertCount(2, $params);
|
||||
$this->assertEquals('Domain\Order\OrderRepository', $params[0]->getType()->getName());
|
||||
$this->assertEquals('Domain\Order\OrderAdminService', $params[1]->getType()->getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
<b>ver. 0.294 - 18.02.2026</b><br />
|
||||
- UPDATE - usuniecie 12 legacy klas z autoload/shop/ (~2363 linii) — pelna migracja na Domain-Driven Architecture
|
||||
- UPDATE - class.Order.php: logika Apilo sync i email statusu przeniesiona do OrderAdminService
|
||||
- UPDATE - class.Product.php: ~20 metod przeniesionych do ProductRepository, calculate_basket_product_price do BasketCalculator
|
||||
- FIX - findCached(): stale Redis cache z obiektami \shop\Product powodowal ceny 0,00 zl
|
||||
- FIX - LayoutEngine: niekwalifikowane Product::getFromCache() resolwaly do \front\Product
|
||||
- FIX - szablony: konwersja object access na array access po migracji Product
|
||||
- UPDATE - AttributeRepository::getAttributeValueById() — dodano Redis cache
|
||||
- CLEANUP - katalog autoload/shop/ pusty, zero referencji \shop\ w aktywnym kodzie
|
||||
<hr>
|
||||
<b>ver. 0.293 - 17.02.2026</b><br />
|
||||
- UPDATE - front\controls\Site przemianowany na front\App (router, camelCase: checkUrlParams, pageTitle)
|
||||
- UPDATE - front\view\Site przemianowany na front\LayoutEngine (layout engine, camelCase: cookieInformation)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?
|
||||
$current_ver = 293;
|
||||
$current_ver = 294;
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user