Compare commits

..

11 Commits

Author SHA1 Message Date
708a941f13 ver. 0.307: check-update button + auto-generated changelog
- Add "Sprawdź aktualizacje" refresh button in admin sidebar (AJAX check without page reload)
- Add UpdateController::checkUpdate() action clearing session cache and querying update server
- Replace hand-edited changelog.php with auto-generating script (reads manifests + legacy JSON)
- Migrate all legacy changelog entries (0.300-0.001) to changelog-legacy.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:01:12 +01:00
0b1f289478 build: rebuild update packages v0.304, v0.305 with fixed .updateignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:16:26 +01:00
82a655a6af build: update package v0.306
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:13:01 +01:00
f16f5ce8f8 ver. 0.306: hide transport methods with no available payment methods
When all payment methods for a transport are filtered out by
min_order_amount/max_order_amount limits, the transport is now hidden
from the basket. Prevents showing delivery options with empty payment
method lists (e.g. "Kurier - płatność przy odbiorze" when COD exceeds
max amount).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:09:38 +01:00
2dc1360395 remove ver_0.304_sql.txt from updates/ — SQL lives only in migrations/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:50:29 +01:00
5c1842181a fix: UTF-8 BOM in update SQL files causing MariaDB syntax error
PowerShell 5.1 Out-File -Encoding UTF8 adds BOM (EF BB BF) which
breaks SQL execution on production. Also fix manifest JSON serializing
full PS objects instead of plain strings.

- build-update.ps1: use UTF8Encoding($false) for all file writes
- build-update.ps1: force .ToString() on Get-Content results
- UpdateRepository.php: strip BOM and normalize line endings in executeSql
- Rebuild ver_0.304 package files (clean SQL + manifest)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:49:23 +01:00
62255541ab build: update packages v0.304, v0.305
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:39:20 +01:00
ec77160130 ver. 0.305: Fix permutation attribute sorting + free delivery progress bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 16:33:30 +01:00
562495f120 build: update package v0.304
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:28:19 +01:00
9de4afec9a ver. 0.304: Configurable payment method order amount limits
Replace hardcoded PayPo condition (id=6, 40-1000 PLN) with generic
min/max order amount columns on pp_shop_payment_methods. Admin form
fields added, frontend basket checkout filters dynamically. Cache
invalidation on save. 4 new tests (734 total, 2080 assertions).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:26:51 +01:00
3a3c2adb47 build: update package v0.303
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:05:19 +01:00
40 changed files with 4221 additions and 804 deletions

View File

@@ -18,9 +18,7 @@ test.ps1
memory/
# Infrastruktura aktualizacji (meta, nie runtime)
updates/changelog.php
updates/versions.php
updates/install.php
updates/
.updateignore
build-update.ps1
migrations/
@@ -31,6 +29,13 @@ config.php
admin/.htaccess
libraries/version.ini
# Lokalne style
layout/style-css/style.css
layout/style-css/style.css.map
layout/style-scss/style.scss
layout/style-scss/_mixins.scss
layout/style-scss/_mixins.css
# Temp / cache / backups
temp/
backups/

View File

@@ -11,7 +11,10 @@ Gdy użytkownik napisze `KONIEC PRACY`, wykonaj kolejno:
- `docs/FORM_EDIT_SYSTEM.md`
- `docs/CHANGELOG.md`
- `docs/TESTING.md`
3. Przygotowanie aktualizacji zgodnie z plikiem docs/UPDATE_INSTRUCTIONS.md (ZIP, plik z usuwanymi plikami, plik SQL jeśli wymagany).
3. Migracje SQL (jeśli były zmiany w bazie danych):
- Plik: `migrations/{version}.sql` (np. `migrations/0.304.sql`)
- **NIE** w `updates/` — build script sam wczyta z `migrations/`
- Sprawdź czy plik istnieje i jest poprawnie nazwany przed commitem
4. Commit.
5. Push.

View File

@@ -36,7 +36,7 @@ composer test
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
Current suite: **730 tests, 2066 assertions**.
Current suite: **739 tests, 2089 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.
@@ -208,7 +208,7 @@ $controller = new \admin\Controllers\ExampleController($repo);
When user says **"KONIEC PRACY"**, execute in order:
1. Run tests
2. Update documentation if needed: `docs/DATABASE_STRUCTURE.md`, `docs/PROJECT_STRUCTURE.md`, `docs/FORM_EDIT_SYSTEM.md`, `docs/CHANGELOG.md`, `docs/TESTING.md`
3. Prepare update package per `docs/UPDATE_INSTRUCTIONS.md`
3. SQL migrations (if DB changes): place in `migrations/{version}.sql` (e.g. `migrations/0.304.sql`). **NOT** in `updates/` — build script reads from `migrations/` automatically
4. Commit
5. Push

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1351,39 +1351,6 @@ li.sort-collapsed.sort-hover div {
}
}
input[type="checkbox"] {
position: relative;
width: 40px;
height: 20px;
-webkit-appearance: none;
background: $cGrayLight;
outline: none;
border-radius: 10px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, .2);
}
input:checked[type="checkbox"] {
background: $cMenuText;
}
input[type="checkbox"]:before {
content: '';
position: absolute;
width: 20px;
height: 20px;
border-radius: 10px;
top: 0;
left: 0;
background: #fff;
transform: scale(1.1);
box-shadow: 0 2px 5px rgba(0, 0, 0, .2);
transition: .5s;
}
input:checked[type="checkbox"]:before {
left: 20px;
}
#images-uploader,
#files-uploader {
clear: both;

View File

@@ -81,9 +81,6 @@
</div>
</div>
</div>
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css">
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.min.js"></script>
<script type="text/javascript">
$( function()
{

View File

@@ -18,7 +18,8 @@
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css">
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/square/blue.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css">
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css">
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js"></script>
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
@@ -43,10 +44,16 @@
<div class="menu">
<div class="logo sticky-top">
shop<b>Pro</b>
<span>ver. <?= \Shared\Helpers\Helpers::get_version();?></span><br>
<? if ( $settings[ 'update' ] and \Shared\Helpers\Helpers::get_new_version() > \Shared\Helpers\Helpers::get_version() ):?>
<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>
<? endif;?>
<span>ver. <?= \Shared\Helpers\Helpers::get_version();?>
<? if ( $settings[ 'update' ] ):?>
<i class="fa fa-refresh check-update-btn" id="check-update-btn" title="Sprawdź aktualizacje" style="cursor:pointer;margin-left:4px;font-size:11px;opacity:0.7;"></i>
<? endif;?>
</span><br>
<span id="update-badge-wrap">
<? if ( $settings[ 'update' ] and \Shared\Helpers\Helpers::get_new_version() > \Shared\Helpers\Helpers::get_version() ):?>
<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>
<? endif;?>
</span>
</div>
<div class="menu-content">
<ul>
@@ -355,6 +362,32 @@
});
})();
(function() {
$(document).off('click.checkUpdate', '#check-update-btn').on('click.checkUpdate', '#check-update-btn', function(e) {
e.preventDefault();
var $btn = $(this);
if ($btn.hasClass('fa-spin')) return;
$btn.addClass('fa-spin').css('opacity', 1);
$.ajax({
url: '/admin/update/checkUpdate/',
type: 'GET',
dataType: 'json',
success: function(data) {
$btn.removeClass('fa-spin').css('opacity', 0.7);
var $wrap = $('#update-badge-wrap');
if (data.has_update) {
$wrap.html('<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>');
} else {
$wrap.html('');
}
},
error: function() {
$btn.removeClass('fa-spin').css('opacity', 0.7);
}
});
});
})();
$(document).ready(function () {
var user_agent = navigator.userAgent.toLowerCase();
var click_event = user_agent.match(/(iphone|ipod|ipad)/) ? "touchend" : "click";

View File

@@ -94,8 +94,13 @@ class BasketCalculator
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'] );
if ( !$permutation and isset( $val['attributes'] ) and is_array( $val['attributes'] ) and count( $val['attributes'] ) ) {
$attrs = $val['attributes'];
usort( $attrs, function ( $a, $b ) {
return (int) explode( '-', $a )[0] - (int) explode( '-', $b )[0];
} );
$permutation = implode( '|', $attrs );
}
$quantity_options = $productRepo->getProductPermutationQuantityOptions(
$val['parent_id'] ? $val['parent_id'] : $val['product-id'],

View File

@@ -120,10 +120,16 @@ class PaymentMethodRepository
'description' => trim((string)($data['description'] ?? '')),
'status' => $this->toSwitchValue($data['status'] ?? 0),
'apilo_payment_type_id' => $this->normalizeApiloPaymentTypeId($data['apilo_payment_type_id'] ?? null),
'min_order_amount' => $this->normalizeDecimalOrNull($data['min_order_amount'] ?? null),
'max_order_amount' => $this->normalizeDecimalOrNull($data['max_order_amount'] ?? null),
];
$this->db->update('pp_shop_payment_methods', $row, ['id' => $paymentMethodId]);
$cacheHandler = new \Shared\Cache\CacheHandler();
$cacheHandler->deletePattern('payment_method*');
$cacheHandler->deletePattern('payment_methods*');
return $paymentMethodId;
}
@@ -232,7 +238,9 @@ class PaymentMethodRepository
spm.name,
spm.description,
spm.status,
spm.apilo_payment_type_id
spm.apilo_payment_type_id,
spm.min_order_amount,
spm.max_order_amount
FROM pp_shop_payment_methods AS spm
INNER JOIN pp_shop_transport_payment_methods AS stpm
ON stpm.id_payment_method = spm.id
@@ -325,6 +333,8 @@ class PaymentMethodRepository
$row['description'] = (string)($row['description'] ?? '');
$row['status'] = $this->toSwitchValue($row['status'] ?? 0);
$row['apilo_payment_type_id'] = $this->normalizeApiloPaymentTypeId($row['apilo_payment_type_id'] ?? null);
$row['min_order_amount'] = $this->normalizeDecimalOrNull($row['min_order_amount'] ?? null);
$row['max_order_amount'] = $this->normalizeDecimalOrNull($row['max_order_amount'] ?? null);
return $row;
}
@@ -350,6 +360,23 @@ class PaymentMethodRepository
return $text;
}
/**
* @return float|null
*/
private function normalizeDecimalOrNull($value)
{
if ($value === null || $value === false) {
return null;
}
$text = trim((string)$value);
if ($text === '') {
return null;
}
return (float)$text;
}
private function toSwitchValue($value): int
{
if (is_bool($value)) {

View File

@@ -323,7 +323,9 @@ class TransportRepository
$transports[] = $tr;
}
if ( \Shared\Helpers\Helpers::normalize_decimal( \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) )
$products_summary = (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon );
if ( \Shared\Helpers\Helpers::normalize_decimal( $products_summary ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) )
{
for ( $i = 0; $i < count( $transports ); $i++ ) {
if ( $transports[$i]['delivery_free'] == 1 ) {
@@ -332,7 +334,39 @@ class TransportRepository
}
}
return $transports;
// Ukryj transporty, dla których nie ma żadnej dostępnej formy płatności
$paymentMethodRepo = new \Domain\PaymentMethod\PaymentMethodRepository( $this->db );
$filtered = [];
foreach ( $transports as $tr )
{
$paymentMethods = $paymentMethodRepo->paymentMethodsByTransport( $tr['id'] );
$order_total = $products_summary + (float)$tr['cost'];
$has_available_pm = false;
foreach ( $paymentMethods as $pm )
{
$min = isset( $pm['min_order_amount'] ) ? (float)$pm['min_order_amount'] : null;
$max = isset( $pm['max_order_amount'] ) ? (float)$pm['max_order_amount'] : null;
$available = true;
if ( $min !== null && $min > 0 && $order_total < $min ) $available = false;
if ( $max !== null && $max > 0 && $order_total > $max ) $available = false;
if ( $available )
{
$has_available_pm = true;
break;
}
}
if ( $has_available_pm )
{
$filtered[] = $tr;
}
}
return $filtered;
}
/**

View File

@@ -416,7 +416,12 @@ class UpdateRepository
return $log;
}
$queries = explode( PHP_EOL, $response );
// Usunięcie UTF-8 BOM i normalizacja końców linii
$response = ltrim( $response, "\xEF\xBB\xBF" );
$response = str_replace( "\r\n", "\n", $response );
$response = str_replace( "\r", "\n", $response );
$queries = explode( "\n", $response );
$log[] = '[OK] Pobrano ' . count( $queries ) . ' zapytań SQL';
$success = 0;
$errors = 0;

View File

@@ -60,4 +60,9 @@ class Tpl
{
return $this->vars[$name];
}
public function __isset($name)
{
return isset($this->vars[$name]);
}
}

View File

@@ -182,6 +182,8 @@ class ShopPaymentMethodController
'description' => (string)($paymentMethod['description'] ?? ''),
'status' => (int)($paymentMethod['status'] ?? 0),
'apilo_payment_type_id' => $paymentMethod['apilo_payment_type_id'] ?? '',
'min_order_amount' => $paymentMethod['min_order_amount'] ?? '',
'max_order_amount' => $paymentMethod['max_order_amount'] ?? '',
];
$fields = [
@@ -203,6 +205,16 @@ class ShopPaymentMethodController
'tab' => 'settings',
'rows' => 5,
]),
FormField::number('min_order_amount', [
'label' => 'Min. kwota zamowienia (PLN)',
'tab' => 'settings',
'step' => 0.01,
]),
FormField::number('max_order_amount', [
'label' => 'Maks. kwota zamowienia (PLN)',
'tab' => 'settings',
'step' => 0.01,
]),
FormField::select('apilo_payment_type_id', [
'label' => 'Typ platnosci Apilo',
'tab' => 'settings',

View File

@@ -49,4 +49,17 @@ class UpdateController
echo json_encode( $response );
exit;
}
public function checkUpdate(): void
{
\Shared\Helpers\Helpers::set_session( 'new-version', null );
$newVer = \Shared\Helpers\Helpers::get_new_version();
$curVer = \Shared\Helpers\Helpers::get_version();
echo json_encode( [
'has_update' => $newVer > $curVer,
'new_ver' => $newVer,
] );
exit;
}
}

View File

@@ -132,6 +132,11 @@ class ShopBasketController
$attributes[] = $val;
}
// Sort by attribute ID to match permutation_hash order (generated with ksort)
usort( $attributes, function ( $a, $b ) {
return (int) explode( '-', $a )[0] - (int) explode( '-', $b )[0];
} );
foreach( $values as $key => $val )
{
if ( strpos( $key, 'custom_field' ) !== false )
@@ -372,7 +377,9 @@ class ShopBasketController
'transport_id' => \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->transportMethodsFront( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
'transport_id' => $basket_transport_method_id,
'free_delivery' => (float)($settings['free_delivery'] ?? 0),
'basket_summary' => (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon )
] ),
'payment_method_id' => $payment_method_id,
'basket_details' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
@@ -387,6 +394,8 @@ class ShopBasketController
private function jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id )
{
global $settings;
echo json_encode( [
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
'basket' => $basket,
@@ -398,7 +407,9 @@ class ShopBasketController
'products_count' => count( $basket ),
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
'transports_methods' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->transportMethodsFront( $basket, $coupon ),
'transport_id' => $basket_transport_method_id
'transport_id' => $basket_transport_method_id,
'free_delivery' => (float)($settings['free_delivery'] ?? 0),
'basket_summary' => (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon )
] )
] );
exit;

View File

@@ -80,16 +80,17 @@ class ShopProductController
{
global $lang_id;
$combination = '';
$selected_values = \Shared\Helpers\Helpers::get( 'selected_values' );
foreach ( $selected_values as $value )
{
$combination .= $value;
if ( $value != end( $selected_values ) )
$combination .= '|';
// Sort by attribute ID to match permutation_hash order (generated with ksort)
if ( is_array( $selected_values ) ) {
usort( $selected_values, function ( $a, $b ) {
return (int) explode( '-', $a )[0] - (int) explode( '-', $b )[0];
} );
}
$combination = is_array( $selected_values ) ? implode( '|', $selected_values ) : '';
$product_id = \Shared\Helpers\Helpers::get( 'product_id' );
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
$product = $productRepo->findCached( $product_id, $lang_id );
@@ -102,6 +103,10 @@ class ShopProductController
private static function getPermutation( $attributes )
{
if ( !is_array( $attributes ) || !count( $attributes ) ) return null;
// Sort by attribute ID to match permutation_hash order (generated with ksort)
usort( $attributes, function ( $a, $b ) {
return (int) explode( '-', $a )[0] - (int) explode( '-', $b )[0];
} );
return implode( '|', $attributes );
}

View File

@@ -32,6 +32,7 @@ param(
)
$ErrorActionPreference = "Stop"
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
# --- Helpers ---
@@ -214,7 +215,7 @@ $sqlQueries = @()
$migrationFile = "migrations/$versionNumber.sql"
if (Test-Path $migrationFile) {
$sqlQueries = Get-Content $migrationFile | Where-Object { $_.Trim() -ne '' }
$sqlQueries = @(Get-Content $migrationFile | Where-Object { $_.Trim() -ne '' } | ForEach-Object { $_.ToString() })
Write-Step "Znaleziono migracje SQL: $migrationFile ($($sqlQueries.Count) zapytan)"
} else {
Write-Step "Brak migracji SQL ($migrationFile nie istnieje)"
@@ -322,7 +323,7 @@ $manifest = @{
$manifestJson = $manifest | ConvertTo-Json -Depth 4
$manifestPath = "$updatesDir/ver_${versionNumber}_manifest.json"
$manifestJson | Out-File $manifestPath -Encoding UTF8
[System.IO.File]::WriteAllText($manifestPath, $manifestJson, $Utf8NoBom)
Write-Ok "Utworzono manifest: $manifestPath"
@@ -330,7 +331,7 @@ Write-Ok "Utworzono manifest: $manifestPath"
if ($sqlQueries.Count -gt 0) {
$sqlPath = "$updatesDir/ver_${versionNumber}_sql.txt"
($sqlQueries -join "`n") | Out-File $sqlPath -Encoding UTF8 -NoNewline
[System.IO.File]::WriteAllText($sqlPath, ($sqlQueries -join "`n"), $Utf8NoBom)
Write-Ok "Utworzono legacy SQL: $sqlPath"
}
@@ -340,7 +341,7 @@ if ($deletedFilesOnly.Count -gt 0 -or $deletedDirs.Count -gt 0) {
foreach ($d in $deletedDirs) { $filesContent += "D: ../$d" }
$filesPath = "$updatesDir/ver_${versionNumber}_files.txt"
($filesContent -join "`n") | Out-File $filesPath -Encoding UTF8 -NoNewline
[System.IO.File]::WriteAllText($filesPath, ($filesContent -join "`n"), $Utf8NoBom)
Write-Ok "Utworzono legacy files: $filesPath"
}
@@ -350,7 +351,7 @@ $versionsFile = "updates/versions.php"
if (Test-Path $versionsFile) {
$content = Get-Content $versionsFile -Raw
$content = $content -replace '\$current_ver\s*=\s*\d+;', "`$current_ver = $versionInt;"
$content | Out-File $versionsFile -Encoding UTF8 -NoNewline
[System.IO.File]::WriteAllText($versionsFile, $content, $Utf8NoBom)
Write-Ok "Zaktualizowano versions.php: `$current_ver = $versionInt"
}
@@ -363,7 +364,7 @@ if (Test-Path $changelogFile) {
$changelogContent = Get-Content $changelogFile -Raw
$changelogContent = $newEntry + $changelogContent
$changelogContent | Out-File $changelogFile -Encoding UTF8 -NoNewline
[System.IO.File]::WriteAllText($changelogFile, $changelogContent, $Utf8NoBom)
Write-Ok "Zaktualizowano changelog.php"
}

View File

@@ -4,6 +4,45 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
---
## ver. 0.307 (2026-02-22) - Przycisk sprawdzania aktualizacji + auto-changelog
- **NEW**: Przycisk "Sprawdz aktualizacje" w panelu admina — ikona odswiezenia obok numeru wersji, klik odpytuje serwer AJAX-em i pokazuje/ukrywa badge "aktualizacja" bez przeladowania strony
- **NEW**: `UpdateController::checkUpdate()` — nowa akcja AJAX czyszczaca sesje `new-version` i sprawdzajaca wersje z serwera
- **NEW**: `updates/changelog.php` — auto-generowany z manifestow JSON (`_manifest.json`) + `changelog-legacy.json`, zamiast recznej edycji HTML
- **NEW**: `updates/changelog-legacy.json` — migracja 295 wpisow changelogu (wersje 0.300-0.001) do formatu JSON
- **ZMIANA**: Workflow KONIEC PRACY — usuniety krok recznej edycji `changelog.php` (teraz automatyczny)
---
## ver. 0.306 (2026-02-22) - Ukrywanie form dostawy bez dostepnych platnosci
- **FIX**: Formy dostawy, dla ktorych nie ma zadnej dostepnej formy platnosci (np. wszystkie odfiltrowane przez `min_order_amount`/`max_order_amount`), sa teraz ukrywane z listy w koszyku
- **ZMIANA**: `TransportRepository::transportMethodsFront()` — po filtrze wagowym i korekcie darmowej dostawy, dodano filtr sprawdzajacy dostepnosc form platnosci per transport (z wykorzystaniem `PaymentMethodRepository::paymentMethodsByTransport()` z cache Redis)
- **NEW**: 5 nowych testow jednostkowych (739 total, 2089 assertions)
---
## ver. 0.305 (2026-02-22) - Sortowanie permutacji + pasek darmowej dostawy
- **FIX**: Naprawa kolejnosci atrybutow permutacji — sortowanie po ID atrybutu (`usort` wg pierwszego segmentu przed `-`) we wszystkich miejscach: koszyk, kombinacje AJAX, `getPermutation()`, `BasketCalculator`. Zapewnia zgodnosc z `permutation_hash` generowanym przez `ksort`.
- **NEW**: Pasek postepu darmowej dostawy w koszyku — progress bar z ikona, tekstem i animacja. Pokazuje ile brakuje do progu `free_delivery` z ustawien sklepu.
- **FIX**: icheck CSS/JS przeniesione z szablonu `product-combination` do globalnego layoutu admina (`main-layout.php`)
- **FIX**: `Tpl::__isset()` — obsluga `isset()` na zmiennych szablonu
- **.updateignore** — lokalne style frontendowe dodane do listy ignorowania
---
## ver. 0.304 (2026-02-22) - Konfigurowalne limity kwotowe metod platnosci
- **NEW**: Kolumny `min_order_amount` i `max_order_amount` w `pp_shop_payment_methods` — konfigurowalne limity kwotowe per metoda platnosci
- **NEW**: Pola min/max kwoty zamowienia w formularzu edycji metody platnosci (admin)
- **FIX**: Zastapiono hardcoded warunek PayPo (id=6, 40-1000 PLN) generycznym filtrowaniem na froncie (basket checkout)
- **NEW**: Cache invalidation po zapisie metody platnosci
- **NEW**: 4 nowe testy jednostkowe (734 total, 2080 assertions)
- **MIGRATION**: `migrations/0.304.sql` — ALTER TABLE pp_shop_payment_methods ADD min/max_order_amount
---
## ver. 0.303 (2026-02-22) - Fix: wyswietlanie atrybutow produktu na froncie + podglad produktu w adminie
- **FIX**: Naprawiono wyswietlanie atrybutow produktu na froncie — gdy dwa atrybuty mialy te sama wartosc kolejnosci (`o`), jeden nadpisywal drugi (kolizja kluczy tablicy). Teraz atrybuty sortowane przez `usort()` z unikalnymi kluczami sekwencyjnymi.

View File

@@ -508,12 +508,16 @@ Metody platnosci sklepu (modul `/admin/shop_payment_method`).
| description | Opis metody platnosci (wyswietlany m.in. w checkout) |
| status | Status: 1 = aktywna, 0 = nieaktywna |
| apilo_payment_type_id | ID typu platnosci Apilo (NULL gdy brak mapowania) |
| min_order_amount | Minimalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
| max_order_amount | Maksymalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
| sellasist_payment_type_id | DEPRECATED (integracja Sellasist usunieta w ver. 0.263) |
**Uzywane w:** `Domain\PaymentMethod\PaymentMethodRepository`, `admin\Controllers\ShopPaymentMethodController`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod`, `admin\controls\ShopTransport`, `cron.php`
**Aktualizacja 2026-02-14 (ver. 0.268):** modul `/admin/shop_payment_method` korzysta z `Domain\PaymentMethod\PaymentMethodRepository` przez `admin\Controllers\ShopPaymentMethodController`. Usunieto legacy klasy `admin\controls\ShopPaymentMethod`, `admin\factory\ShopPaymentMethod`, `admin\view\ShopPaymentMethod` oraz widok `admin/templates/shop-payment-method/view-list.php`.
**Aktualizacja 2026-02-22 (ver. 0.304):** dodano kolumny `min_order_amount` i `max_order_amount` — konfigurowalne limity kwotowe metod platnosci. Zastapiono hardcoded warunek PayPo (id=6, 40-1000 PLN) generycznym filtrowaniem na froncie.
## pp_shop_transports
Rodzaje transportu sklepu (modul `/admin/shop_transport`).

View File

@@ -23,10 +23,10 @@ composer test # standard
## Aktualny stan
```text
OK (730 tests, 2066 assertions)
OK (739 tests, 2089 assertions)
```
Zweryfikowano: 2026-02-21 (ver. 0.300)
Zweryfikowano: 2026-02-22 (ver. 0.304)
## Konfiguracja

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2349,6 +2349,60 @@ ul.pager {
white-space: nowrap;
}
}
.free-delivery-bar {
background: #3a3a3a;
border-radius: 8px;
padding: 15px 20px;
margin-top: 15px;
display: flex;
align-items: center;
gap: 15px;
color: #fff;
&.success {
background: #28a745;
}
&__icon {
font-size: 28px;
flex-shrink: 0;
}
&__content {
flex: 1;
}
&__text {
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
}
&__progress {
height: 8px;
background: #555;
border-radius: 4px;
overflow: hidden;
}
&__progress-fill {
height: 100%;
background: linear-gradient(90deg, #28a745, #5cb85c);
border-radius: 4px;
transition: width 0.3s ease;
}
&__remaining {
font-size: 12px;
margin-top: 5px;
color: #ccc;
strong {
color: #ff9800;
}
}
}
}
}
}

2
migrations/0.304.sql Normal file
View File

@@ -0,0 +1,2 @@
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
ALTER TABLE pp_shop_payment_methods ADD COLUMN max_order_amount DECIMAL(10,2) DEFAULT NULL;

View File

@@ -13,7 +13,14 @@
$basket_summary = \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) + $transport_cost;
?>
<? if ( is_array( $this -> payment_methods ) ): foreach ( $this -> payment_methods as $payment_method ):?>
<? if ( $payment_method['id'] != 6 or $payment_method['id'] == 6 and $basket_summary >= 40 and $basket_summary <= 1000 ):?>
<?
$min = isset($payment_method['min_order_amount']) ? (float)$payment_method['min_order_amount'] : null;
$max = isset($payment_method['max_order_amount']) ? (float)$payment_method['max_order_amount'] : null;
$show = true;
if ($min !== null && $min > 0 && $basket_summary < $min) $show = false;
if ($max !== null && $max > 0 && $basket_summary > $max) $show = false;
?>
<? if ( $show ):?>
<div class="options">
<div class="check">
<input type="radio" class="icheck" name="payment_method" value="<?= $payment_method['id'];?>"

View File

@@ -52,6 +52,32 @@ if ( is_array( $this -> transports_methods ) )
</div>
<? endif;?>
<? endforeach; endif;?>
<?php if ( $this->free_delivery > 0 ): ?>
<?php
$percentage = min(100, ($this->basket_summary / $this->free_delivery) * 100);
$remaining = $this->free_delivery - $this->basket_summary;
?>
<div class="free-delivery-bar <?= $percentage >= 100 ? 'success' : '' ?>">
<div class="free-delivery-bar__icon">&#x1F69A;</div>
<div class="free-delivery-bar__content">
<?php if ($percentage >= 100): ?>
<div class="free-delivery-bar__text">Gratulacje! Masz darmow&#261; dostaw&#281;!</div>
<?php else: ?>
<div class="free-delivery-bar__text">
Zr&#243;b zakupy za <?= \Shared\Helpers\Helpers::decimal( $this->free_delivery, 2 );?> z&#322; i otrzymaj darmow&#261; dostaw&#281;
</div>
<?php endif; ?>
<div class="free-delivery-bar__progress">
<div class="free-delivery-bar__progress-fill" style="width: <?= $percentage ?>%"></div>
</div>
<?php if ($percentage < 100): ?>
<div class="free-delivery-bar__remaining">
Brakuje <strong><?= \Shared\Helpers\Helpers::decimal( $remaining, 2 );?> z&#322;</strong> do darmowej dostawy.
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<script class="footer" type="text/javascript">
$( function() {

View File

@@ -78,6 +78,8 @@ class PaymentMethodRepositoryTest extends TestCase
$this->assertSame('test', $updateRow['description']);
$this->assertSame(1, $updateRow['status']);
$this->assertSame(22, $updateRow['apilo_payment_type_id']);
$this->assertNull($updateRow['min_order_amount']);
$this->assertNull($updateRow['max_order_amount']);
$this->assertSame(['id' => 3], $updateWhere);
}
@@ -113,6 +115,102 @@ class PaymentMethodRepositoryTest extends TestCase
$this->assertNull($repository->save(0, ['status' => 1]));
}
public function testSavePersistsMinMaxOrderAmount(): void
{
$mockDb = $this->createMock(\medoo::class);
$updateRow = null;
$mockDb->expects($this->once())
->method('update')
->willReturnCallback(function ($table, $row) use (&$updateRow) {
$updateRow = $row;
return true;
});
$repository = new PaymentMethodRepository($mockDb);
$repository->save(5, [
'description' => 'test',
'status' => 1,
'apilo_payment_type_id' => '',
'min_order_amount' => '40.00',
'max_order_amount' => '1000.00',
]);
$this->assertSame(40.0, $updateRow['min_order_amount']);
$this->assertSame(1000.0, $updateRow['max_order_amount']);
}
public function testSaveConvertsEmptyMinMaxToNull(): void
{
$mockDb = $this->createMock(\medoo::class);
$updateRow = null;
$mockDb->expects($this->once())
->method('update')
->willReturnCallback(function ($table, $row) use (&$updateRow) {
$updateRow = $row;
return true;
});
$repository = new PaymentMethodRepository($mockDb);
$repository->save(6, [
'description' => 'test',
'status' => 1,
'apilo_payment_type_id' => '',
'min_order_amount' => '',
'max_order_amount' => '',
]);
$this->assertNull($updateRow['min_order_amount']);
$this->assertNull($updateRow['max_order_amount']);
}
public function testFindNormalizesMinMaxOrderAmount(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_payment_methods', '*', ['id' => 6])
->willReturn([
'id' => '6',
'name' => 'PayPo',
'description' => '',
'status' => '1',
'apilo_payment_type_id' => null,
'min_order_amount' => '40.00',
'max_order_amount' => '1000.00',
]);
$repository = new PaymentMethodRepository($mockDb);
$result = $repository->find(6);
$this->assertSame(40.0, $result['min_order_amount']);
$this->assertSame(1000.0, $result['max_order_amount']);
}
public function testFindNormalizesNullMinMaxOrderAmount(): void
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->expects($this->once())
->method('get')
->with('pp_shop_payment_methods', '*', ['id' => 7])
->willReturn([
'id' => '7',
'name' => 'Przelew',
'description' => '',
'status' => '1',
'apilo_payment_type_id' => null,
'min_order_amount' => null,
'max_order_amount' => null,
]);
$repository = new PaymentMethodRepository($mockDb);
$result = $repository->find(7);
$this->assertNull($result['min_order_amount']);
$this->assertNull($result['max_order_amount']);
}
public function testListForAdminWhitelistsSortAndDirection(): void
{
$mockDb = $this->createMock(\medoo::class);

View File

@@ -420,4 +420,197 @@ class TransportRepositoryTest extends TestCase
$this->assertEquals([], $repository->forPaymentMethod(0));
}
// =========================================================================
// transportMethodsFront — filtrowanie po dostępności form płatności
// =========================================================================
private function createMockDbForTransportMethodsFront(array $transports, array $paymentMethodsByTransportId): \medoo
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')
->willReturn($transports);
$mockDb->method('query')
->willReturnCallback(function ($sql, $params) use ($paymentMethodsByTransportId) {
$transportId = $params[':transport_id'] ?? 0;
$rows = $paymentMethodsByTransportId[$transportId] ?? [];
return new class($rows) {
private $rows;
public function __construct($rows) { $this->rows = $rows; }
public function fetchAll() { return $this->rows; }
};
});
return $mockDb;
}
private function makeTransport(int $id, float $cost, int $deliveryFree = 0, $maxWp = null, int $default = 0): array
{
return [
'id' => (string)$id, 'name' => 'Transport ' . $id, 'name_visible' => '',
'description' => '', 'status' => '1', 'cost' => (string)$cost,
'max_wp' => $maxWp !== null ? (string)$maxWp : null,
'default' => (string)$default, 'delivery_free' => (string)$deliveryFree,
'apilo_carrier_account_id' => null, 'o' => (string)$id,
];
}
private function makePaymentMethod(int $id, $minAmount = null, $maxAmount = null): array
{
return [
'id' => $id, 'name' => 'Payment ' . $id, 'description' => '',
'status' => 1, 'apilo_payment_type_id' => null,
'min_order_amount' => $minAmount !== null ? (string)$minAmount : null,
'max_order_amount' => $maxAmount !== null ? (string)$maxAmount : null,
];
}
/**
* Transport z formą płatności przekraczającą max_order_amount powinien być ukryty
*/
public function testTransportMethodsFrontHidesTransportWhenAllPaymentsExceedMaxAmount(): void
{
global $settings;
$settings = ['free_delivery' => 9999];
$GLOBALS['mdb'] = $this->createMock(\medoo::class);
$transports = [
$this->makeTransport(1, 15.00, 0, null, 1),
$this->makeTransport(2, 20.00),
];
$paymentMethods = [
1 => [$this->makePaymentMethod(1)],
2 => [$this->makePaymentMethod(3, null, 10.00)],
];
$mockDb = $this->createMockDbForTransportMethodsFront($transports, $paymentMethods);
$repository = new TransportRepository($mockDb);
// Pusty koszyk: summaryPrice=0, transport 2 cost=20, order_total=20, max=10 → ukryty
$result = $repository->transportMethodsFront([], null);
$this->assertCount(1, $result);
$this->assertSame(1, $result[0]['id']);
}
/**
* Transport z formą płatności poniżej min_order_amount powinien być ukryty
*/
public function testTransportMethodsFrontHidesTransportWhenAllPaymentsBelowMinAmount(): void
{
global $settings;
$settings = ['free_delivery' => 9999];
$GLOBALS['mdb'] = $this->createMock(\medoo::class);
$transports = [
$this->makeTransport(1, 10.00, 0, null, 1),
$this->makeTransport(2, 10.00),
];
$paymentMethods = [
1 => [$this->makePaymentMethod(1)],
2 => [$this->makePaymentMethod(3, 100.00, null)],
];
$mockDb = $this->createMockDbForTransportMethodsFront($transports, $paymentMethods);
$repository = new TransportRepository($mockDb);
// Pusty koszyk: summaryPrice=0, transport 2 cost=10, order_total=10, min=100 → ukryty
$result = $repository->transportMethodsFront([], null);
$this->assertCount(1, $result);
$this->assertSame(1, $result[0]['id']);
}
/**
* Transport bez żadnych form płatności powinien być ukryty
*/
public function testTransportMethodsFrontHidesTransportWithNoPaymentMethods(): void
{
global $settings;
$settings = ['free_delivery' => 9999];
$GLOBALS['mdb'] = $this->createMock(\medoo::class);
$transports = [
$this->makeTransport(1, 10.00, 0, null, 1),
$this->makeTransport(2, 10.00),
];
$paymentMethods = [
1 => [$this->makePaymentMethod(1)],
2 => [],
];
$mockDb = $this->createMockDbForTransportMethodsFront($transports, $paymentMethods);
$repository = new TransportRepository($mockDb);
$result = $repository->transportMethodsFront([], null);
$this->assertCount(1, $result);
$this->assertSame(1, $result[0]['id']);
}
/**
* Oba transporty z dostępnymi płatnościami — oba widoczne
*/
public function testTransportMethodsFrontKeepsBothTransportsWhenPaymentsAvailable(): void
{
global $settings;
$settings = ['free_delivery' => 9999];
$GLOBALS['mdb'] = $this->createMock(\medoo::class);
$transports = [
$this->makeTransport(1, 10.00, 0, null, 1),
$this->makeTransport(2, 15.00),
];
$paymentMethods = [
1 => [$this->makePaymentMethod(1)],
2 => [$this->makePaymentMethod(2), $this->makePaymentMethod(3, 50.00, null)],
];
$mockDb = $this->createMockDbForTransportMethodsFront($transports, $paymentMethods);
$repository = new TransportRepository($mockDb);
// Transport 2 ma 2 płatności: jedna bez limitów (dostępna), druga z min=50 (niedostępna)
// Wystarczy 1 dostępna → transport widoczny
$result = $repository->transportMethodsFront([], null);
$this->assertCount(2, $result);
}
/**
* Transport z wieloma płatnościami — przynajmniej jedna dostępna = transport widoczny
*/
public function testTransportMethodsFrontKeepsTransportIfAtLeastOnePaymentAvailable(): void
{
global $settings;
$settings = ['free_delivery' => 9999];
$GLOBALS['mdb'] = $this->createMock(\medoo::class);
$transports = [
$this->makeTransport(1, 5.00, 0, null, 1),
];
// 3 płatności: 2 niedostępne (min za wysoki, max za niski), 1 dostępna (brak limitów)
$paymentMethods = [
1 => [
$this->makePaymentMethod(1, 100.00, null),
$this->makePaymentMethod(2, null, 1.00),
$this->makePaymentMethod(3),
],
];
$mockDb = $this->createMockDbForTransportMethodsFront($transports, $paymentMethods);
$repository = new TransportRepository($mockDb);
$result = $repository->transportMethodsFront([], null);
$this->assertCount(1, $result);
$this->assertSame(1, $result[0]['id']);
}
}

BIN
updates/0.30/ver_0.303.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"changelog": "FIX - naprawiono wyswietlanie atrybutow produktu na froncie (kolizja kolejnosci), NEW - przycisk Podglad w edycji produktu",
"version": "0.303",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"admin/templates/components/form-edit.php",
"autoload/Domain/Product/ProductRepository.php",
"autoload/admin/Controllers/ShopProductController.php",
"autoload/admin/ViewModels/Forms/FormAction.php"
]
},
"checksum_zip": "sha256:6d8cc0c3419c50345d9c4fa1f526a09004194cbf7f76302f3cfe7afe64b00545",
"sql": [
],
"date": "2026-02-22",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.304.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"changelog": "NEW - konfigurowalne limity kwotowe metod platnosci (min/max kwota zamowienia)",
"version": "0.304",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"autoload/Domain/PaymentMethod/PaymentMethodRepository.php",
"autoload/admin/Controllers/ShopPaymentMethodController.php",
"templates/shop-basket/basket-payments-methods.php"
]
},
"checksum_zip": "sha256:86ce0ba4f6e46c380b1cfcefe2d5b206aa9bf26f25e62661d1fac3144fa35f07",
"sql": [
"ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;",
"ALTER TABLE pp_shop_payment_methods ADD COLUMN max_order_amount DECIMAL(10,2) DEFAULT NULL;"
],
"date": "2026-02-22",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.305.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,24 @@
{
"version": "0.305",
"date": "2026-02-22",
"checksum_zip": "sha256:05c06817cc7fdbf362e84be5a547010b9cf9eedba423d8d23fc5de2308bec084",
"files": {
"added": [],
"modified": [
"admin/layout/style-css/style.css",
"admin/layout/style-css/style.css.map",
"admin/layout/style-scss/style.scss",
"admin/templates/shop-product/product-combination.php",
"admin/templates/site/main-layout.php",
"autoload/Domain/Basket/BasketCalculator.php",
"autoload/Shared/Tpl/Tpl.php",
"autoload/front/Controllers/ShopBasketController.php",
"autoload/front/Controllers/ShopProductController.php",
"templates/shop-basket/basket-transport-methods.php"
],
"deleted": []
},
"directories_deleted": [],
"sql": [],
"changelog": "FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku"
}

BIN
updates/0.30/ver_0.306.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,24 @@
{
"changelog": "FIX - ukrywanie form dostawy gdy nie ma dostepnych form platnosci",
"version": "0.306",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"autoload/Domain/Transport/TransportRepository.php",
"autoload/Domain/Update/UpdateRepository.php"
]
},
"checksum_zip": "sha256:f0fefc2d28c00257f1015fb51bcd9b8b43c04fee4c947f6df1ca04761c61326f",
"sql": [
],
"date": "2026-02-22",
"directories_deleted": [
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,728 +1,72 @@
<b>ver. 0.302 - 22.02.2026</b><br />
NEW - REST API wariantów produktów (CRUD), słownik atrybutów, filtrowanie po atrybutach, wzbogacone atrybuty z tłumaczeniami
<hr>
<b>ver. 0.301 - 22.02.2026</b><br />
NEW - Ukrywalne filtry tabel, mobilna wersja szczegółów zamówienia
<hr>
<b>ver. 0.300 - 21.02.2026</b><br />
- NEW - System aktualizacji oparty na manifestach JSON (checksum SHA256, backup plikĂłw, automatyczny build)
- NEW - Panel logu aktualizacji w panelu admina
<hr>
<b>ver. 0.299 - 21.02.2026</b><br />
- NEW - Ukrywanie/pokazywanie kolumn w tabelach admina (toggle switch + localStorage)
<hr>
<b>ver. 0.298 - 20.02.2026</b><br />
- FIX - kilka poprawek po aktualizacji
<hr>
<b>ver. 0.297 - 19.02.2026</b><br />
- NEW - REST API produktĂłw (lista, szczegĂłĹy, tworzenie, aktualizacja)
- NEW - Endpoint products z filtrowaniem, sortowaniem i paginacjÄ…
- NEW - Partial update produktĂłw (tylko zmienione pola)
<hr>
<b>ver. 0.296 - 19.02.2026</b><br />
- NEW - REST API zamĂłwieĹ„ dla ordersPRO (lista, szczegĂłĹy, zmiana statusu, atnoĹci)
- NEW - Endpointy ownikowe (statusy, transporty, metody atnoĹci)
- NEW - Autentykacja API przez X-Api-Key header
- NEW - Kolumna updated_at w pp_shop_orders (polling zmian)
<hr>
<b>ver. 0.295 - 19.02.2026</b><br />
- NEW - Edycja produktĂłw w zamĂłwieniu z panelu admina (dodawanie, usuwanie, zmiana iloĹci/cen)
- NEW - Wyszukiwarka produktĂłw AJAX w formularzu edycji zamĂłwienia
- NEW - Automatyczna korekta stanĂłw magazynowych i przeliczanie kosztu dostawy
- FIX - Cena promo w zamĂłwieniu = 0 gdy identyczna z cenÄ… bazowÄ…
<hr>
<b>ver. 0.294 - 19.02.2026</b><br />
- FIX - Code review zakończony (96/96 klas, ~1144 metod): 27 fixów across all layers
- FIX - Domain: null guard na query()->fetchAll() w 8 repozytoriach, redundancja DI w PromotionRepository
- FIX - Admin: null safety find() ?: [] w 10 kontrolerach, null guard w App logowaniu/2FA
- FIX - Front: LayoutEngine undefined $level + $_GET null check, ShopBasketController missing global $lang_id
- FIX - Shared: Helpers $_GET null check + bug 'png' → 'image/png' (Imagick lossless WebP nigdy nie dziaĹ)
<hr>
<b>ver. 0.293 - 19.02.2026</b><br />
- FIX - ArticleRepository: SQL injection fix (addslashesâ†parameterized), uproszczenie articleDetailsFrontend
- FIX - AttributeRepository: martwy class_exists('\S') blokowal czyszczenie cache/temp
- FIX - CategoryRepository: martwy class_exists('\S') blokowal generowanie linkow SEO kategorii
- FIX - BannerRepository: parametryzacja dat w SQL + null guard na query()
- FIX - BasketCalculator: null guard checkProductQuantityInStock + opcjonalne DI params summaryPrice/calculateBasketProductPrice
- FIX - PromotionRepository: null guard na $basket (produkcyjny fatal error)
- UPDATE - OrderRepository, ShopBasketController, ajax.php: jawne DI zamiast globals w callerach BasketCalculator
<hr>
<b>ver. 0.292 - 18.02.2026</b><br />
- UPDATE - pelna migracja front\factory\ do Domain (5 ostatnich klas: ShopProduct, ShopPaymentMethod, ShopPromotion, ShopStatuses, ShopTransport)
- UPDATE - ProductRepository: ~20 nowych metod frontendowych (cache Redis, lazy loading, SKU/EAN fallback)
- UPDATE - PromotionRepository: 5 metod aplikowania promocji (applyTypeWholeBasket/CheapestProduct/CategoriesOr/CategoriesAnd/CategoryCondition)
- UPDATE - TransportRepository: 4 metody frontendowe z cache (transportMethodsFront, transportCostCached, findActiveByIdCached, forPaymentMethod)
- UPDATE - PaymentMethodRepository: metody frontendowe z Redis cache
- CLEANUP - usuniety caly folder front\factory\ (20 klas zmigrowanych) + 4 inne klasy legacy
- FIX - broken transports_list() w ajax.php zastapiony nowa metoda forPaymentMethod()
- UPDATE - front\controls\Site przemianowany na front\App (router, camelCase: checkUrlParams, pageTitle)
- UPDATE - front\view\Site przemianowany na front\LayoutEngine (layout engine, camelCase: cookieInformation)
- CLEANUP - usuniete autoload/front/controls/ i autoload/front/view/ (puste foldery + pliki legacy)
- 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 - 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.291 - 17.02.2026</b><br />
- UPDATE - migracja front\controls\ShopProducer + shop\Producer do Domain\Producer\ProducerRepository + front\Controllers\ShopProducerController
- FIX - bug shop\Producer::__get() referowal nieistniejace $this->data
<hr>
<b>ver. 0.290 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\ShopCoupon + front\controls\ShopCoupon do Domain\Coupon\CouponRepository + front\Controllers\ShopCouponController
- UPDATE - migracja front\factory\ShopOrder + front\controls\ShopOrder + front\view\ShopOrder do Domain\Order\OrderRepository + front\Controllers\ShopOrderController
- FIX - kupony jednorazowe nigdy nie byly oznaczane jako uzyte (is_one_time/set_as_used w shop\Coupon)
- FIX - webhooks przelewy24/hotpay ujednolicone z tpay (poprawna obsluga Apilo sync)
<hr>
<b>ver. 0.289 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\ShopCategory + front\view\ShopCategory do Domain\Category\CategoryRepository + front\Views\ShopCategory
- UPDATE - migracja front\factory\ShopClient + front\view\ShopClient + front\controls\ShopClient do Domain\Client\ClientRepository + front\Views\ShopClient + front\Controllers\ShopClientController
- FIX - usuniety hardcoded password bypass 'Legia1916' w logowaniu klienta
<hr>
<b>ver. 0.288 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\ShopBasket do Domain\Basket\BasketCalculator (4 metody statyczne)
- UPDATE - migracja front\controls\ShopBasket do front\Controllers\ShopBasketController (camelCase, instancyjny)
- UPDATE - routing snake_case->camelCase w dispatch dla nowych kontrolerow
- CLEANUP - usunieta klasa cms\Layout (zastapiona $layoutsRepo->find)
- CLEANUP - usuniete 3 klasy legacy (front\factory\ShopBasket, front\controls\ShopBasket, cms\Layout)
<hr>
<b>ver. 0.287 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\Scontainers do Domain\Scontainers\ScontainersRepository (frontScontainerDetails z Redis cache)
- UPDATE - migracja front\factory\ShopAttribute do Domain\Attribute\AttributeRepository (frontAttributeDetails, frontValueDetails z Redis cache)
- CLEANUP - usuniete 3 klasy legacy (front\factory\Scontainers, front\view\Scontainers, front\factory\ShopAttribute)
<hr>
<b>ver. 0.286 - 17.02.2026</b><br />
- UPDATE - migracja front\factory\Layouts do Domain\Layouts\LayoutsRepository (6 metod frontend z Redis cache)
- UPDATE - migracja front\factory\Menu + front\factory\Pages do Domain\Pages\PagesRepository (6 metod frontend z Redis cache)
- UPDATE - migracja front\view\Menu do front\Views\Menu (nowy namespace)
- CLEANUP - usuniete 4 klasy legacy + 1 martwy szablon (submenu.php)
- FIX - null $lang_id przy wczesnym wywolaniu check_url_params()
<hr>
<b>ver. 0.285 - 17.02.2026</b><br />
- UPDATE - migracja class.Tpl.php do Shared\Tpl\Tpl (~135 plikow przepietych)
- CLEANUP - usunieta nieuzywana klasa CurlServer (curl.class.php)
- FIX - thumb.php: naprawa require po migracji Image do Shared namespace
- FIX - Tpl::render() branch 3: sprawdzal templates_user ale ladowal templates
<hr>
<b>ver. 0.284 - 16.02.2026</b><br />
- CLEANUP - usunieta klasa DbModel (base ORM) — logika wbudowana bezposrednio w shop\Promotion
<hr>
<b>ver. 0.283 - 16.02.2026</b><br />
- UPDATE - migracja class.S.php do Shared\Helpers\Helpers (~140 plikow przepietych)
- UPDATE - migracja class.Html.php do Shared\Html\Html
- UPDATE - migracja class.Email.php do Shared\Email\Email
- UPDATE - migracja class.Image.php do Shared\Image\ImageManipulator
- UPDATE - migracja class.Log.php do Shared\Log\Log (usunieta — logika przeniesiona)
- CLEANUP - usunieta class.Mobile_Detect.php (przestarzala detekcja UA)
- CLEANUP - usunieto 12 nieuzywanych metod z klasy S
- FIX - array_cartesian_product() — blad iteracji po niezdefiniowanej zmiennej
<hr>
<b>ver. 0.282 - 16.02.2026</b><br />
- UPDATE - Cache cleanup: eliminacja legacy class.Cache.php, migracja CacheHandler i RedisConnection do Shared\Cache namespace
- UPDATE - 60 odwolan CacheHandler i 12 odwolan RedisConnection przepietych na Shared\Cache\
- UPDATE - 13 metod front\factory przepietych z \Cache::fetch/store na CacheHandler (ShopProduct, ShopPaymentMethod, ShopCategory, ShopTransport, ShopAttribute)
- FIX - naprawione rozbieznosci kluczy cache (random_products, category_name)
- CLEANUP - usuniete: class.Cache.php, class.CacheHandler.php, class.RedisConnection.php
- UPDATE - testy: OK (454 tests, 1449 assertions)
<hr>
<b>ver. 0.281 - 16.02.2026</b><br />
- UPDATE - migracja Banners frontend: factory + view do Domain/Views (DI)
- NEW - `front\Views\Banners` — czysty VIEW (banners, mainBanner)
- UPDATE - `BannerRepository` rozszerzony o 2 metody frontendowe (banners, mainBanner) z Redis cache
- UPDATE - `front\view\Site::show()` przepiety na repo + Views
- CLEANUP - usuniete: front\factory\Banners, front\view\Banners
- UPDATE - testy: OK (454 tests, 1449 assertions)
<hr>
<b>ver. 0.280 - 16.02.2026</b><br />
- UPDATE - migracja Articles frontend: factory + view + encja do Domain/Views (DI)
- NEW - `front\Views\Articles` — czysty VIEW + utility (renderowanie, generateTableOfContents, generateHeadersIds, getImage)
- UPDATE - `ArticleRepository` rozszerzony o 8 metod frontendowych (z Redis cache)
- UPDATE - `front\view\Site::show()` — 5 sekcji przepietych na repo + Views
- UPDATE - `front\controls\Site::route()` — single article + page_type switch przepiete na repo + Views
- UPDATE - 5 szablonow `templates/articles/*` przepietych na `\front\Views\Articles::`
- CLEANUP - usuniete: `class.Article` (encja + metody statyczne), `front\factory\Settings` (fasada)
- FIX - eliminacja `global $lang` z `articleNoindex()`, eliminacja zaleznosci od `front\factory\Pages::page_sort()`
- UPDATE - testy: `OK (450 tests, 1431 assertions)`
<hr>
<b>ver. 0.279 - 16.02.2026</b><br />
- UPDATE - migracja Newsletter frontend: factory + view + controls do Domain/Controllers/Views (DI)
- UPDATE - nowy namespace `front\Controllers` z `NewsletterController` (DI via factory closures)
- UPDATE - nowy namespace `front\Views` z `Languages` i `Newsletter` (czyste VIEW, statyczne metody)
- UPDATE - routing frontend: `Site::getControllerFactories()` z fallback na stare kontrolery
- FIX - `newsletter_unsubscribe()` — poprawiona skladnia medoo `delete()` (2 argumenty zamiast 3)
- UPDATE - eliminacja fasady `front\factory\Languages` — 26 zaleznosci przepietych na `LanguagesRepository`
- CLEANUP - usuniete: `front\factory\Languages`, `front\factory\Newsletter`, `front\view\Languages`, `front\view\Newsletter`, `front\controls\Newsletter`
- UPDATE - testy: `OK (437 tests, 1398 assertions)`
<hr>
<b>ver. 0.278 - 16.02.2026</b><br />
- UPDATE - migracja Settings + Languages do wspolnych klas Domain (z cache Redis)
- FIX - `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `firm_name`)
<hr>
<b>ver. 0.277 - 16.02.2026</b><br />
- NEW - migracja modulu `ShopProduct` (factory) — pelna migracja ~40 metod do `ProductRepository` + ~30 akcji w `ShopProductController`
- NEW - migracja modulu `Dashboard` do Domain + DI (`DashboardRepository`, `DashboardController`)
- NEW - migracja modulu `Update` do Domain + DI (`UpdateRepository`, `UpdateController`)
- UPDATE - klasa `admin\Site` przemianowana na `admin\App` (plik `App.php`)
- UPDATE - refaktoring routingu — usunieto fallback na stare kontrolery, uproszczony routing
- UPDATE - template `update/main-view.php` — panele zamiast `gridEdit`, `$.confirm()` zamiast `$.prompt()`
- CLEANUP - usuniete stare foldery: `autoload/admin/controls/`, `autoload/admin/factory/`, `autoload/admin/view/`
- CLEANUP - usuniete legacy: `class.Dashboard.php` (controls/shop), `class.Update.php` (controls/factory/view), `class.Articles.php` (factory), `class.Page.php` (view), `class.ShopProduct.php` (controls/factory/view)
- UPDATE - `front\factory\Newsletter` przepieta na `ArticleRepository::articlesByDateAdd()`
- UPDATE - testy: `OK (414 tests, 1335 assertions)`
<hr>
<b>ver. 0.276 - 15.02.2026</b><br />
- NEW - migracja modulu `ShopOrder` do architektury Domain + DI (`Domain\Order\OrderRepository`, `Domain\Order\OrderAdminService`, `admin\Controllers\ShopOrderController`)
- UPDATE - modul `/admin/shop_order/*` przepiety na nowy routing (kanoniczny URL `/admin/shop_order/list/`) i nowe widoki (`orders-list`, `order-details`, `order-edit`)
- FIX - stabilizacja listy zamowien (`OrderRepository::listForAdmin`) oraz poprawa wygladu tabeli (`components/table-list`, wyrownanie komorek i `text-right`)
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopOrder.php`, `autoload/admin/factory/class.ShopOrder.php`, `admin/templates/shop-order/view-list.php`
- UPDATE - usunieta fasada `autoload/admin/factory/class.Integrations.php`; wywolania przepiete na `Domain\Integrations\IntegrationsRepository`
- NEW - globalna wyszukiwarka admin (produkty + zamowienia) przy "Wyczysc cache" + endpoint `/admin/settings/globalSearchAjax/`
- FIX - wyszukiwanie po pelnym imieniu i nazwisku w global search
- UPDATE - testy: `OK (385 tests, 1246 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.276.zip`, `ver_0.276_files.txt`
<hr>
<b>ver. 0.275 - 15.02.2026</b><br />
- NEW - migracja modulu `ShopCategory` do architektury Domain + DI (`Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`)
- UPDATE - modul `/admin/shop_category/*` przepiety na nowy routing (kanoniczny URL `/admin/shop_category/list/`) i endpointy AJAX kontrolera (`save_categories_order`, `save_products_order`, `cookie_categories`)
- UPDATE - widoki `shop-category/*`: wydzielenie skryptow do `*-custom-script.php`, ujednolicone strzalki drzewa (`button + caret + aria-expanded`)
- UPDATE - przepiecie zaleznosci `ShopProduct` z `admin\factory\ShopCategory` na `Domain\Category\CategoryRepository`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopCategory.php`, `autoload/admin/factory/class.ShopCategory.php`, `autoload/admin/view/class.ShopCategory.php`
- UPDATE - testy: `OK (377 tests, 1197 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.275.zip`, `ver_0.275_files.txt`
<hr>
<b>ver. 0.274 - 15.02.2026</b><br />
- NEW - migracja modulu `ShopClients` do architektury Domain + DI (`Domain\Client\ClientRepository`, `admin\Controllers\ShopClientsController`)
- UPDATE - modul `/admin/shop_clients/*` przepiety na `components/table-list` (lista klientow i szczegoly zamowien)
- UPDATE - routing i menu admin przepiete na kanoniczny URL `/admin/shop_clients/list/`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopClients.php`, `autoload/admin/factory/class.ShopClients.php`
- UPDATE - testy: `OK (361 tests, 1125 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.274.zip`, `ver_0.274_files.txt`
<hr>
<b>ver. 0.273 - 15.02.2026</b><br />
- NEW - migracja `/admin/shop_product/mass_edit/*` do `Domain\Product\ProductRepository` + `admin\Controllers\ShopProductController` (DI + routing)
- UPDATE - nowy widok/skrypt masowej edycji (`mass-edit`, `mass-edit-custom-script`) z iCheck i ujednoliconymi strzalkami drzewa
- FIX - zaznaczanie kategorii w mass-edit nie zaznacza automatycznie produktow na liscie
- UPDATE - ujednolicenie strzalek/checkboxow w drzewkach: `/admin/pages/list/*` oraz zakladka wyswietlania w `/admin/articles/edit/*`
- UPDATE - testy: `OK (351 tests, 1091 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.273.zip`, `ver_0.273_files.txt`
<hr>
<b>ver. 0.272 - 15.02.2026</b><br />
- NEW - migracja modulu `ShopProductSets` do architektury Domain + DI (`Domain\ProductSet\ProductSetRepository`, `admin\Controllers\ShopProductSetsController`)
- UPDATE - modul `/admin/shop_product_sets/*` przepiety z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit` + multi-select Selectize
- UPDATE - routing i menu admin przepiete na kanoniczny URL `/admin/shop_product_sets/list/`
- UPDATE - `shop\ProductSet` przepiety na fasade do `Domain\ProductSet\ProductSetRepository`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopProductSets.php`, `autoload/admin/factory/class.ShopProductSet.php`, `admin/templates/shop-product-sets/view-list.php`, `admin/templates/shop-product-sets/set-edit.php`
- UPDATE - testy: `OK (324 tests, 1000 assertions)` + nowe pliki testowe `ProductSetRepositoryTest`, `ShopProductSetsControllerTest`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.272.zip`, `ver_0.272_files.txt`
<hr>
<b>ver. 0.271 - 14.02.2026</b><br />
- NEW - migracja modulu `ShopAttribute` do architektury Domain + DI (`Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`)
- UPDATE - modul `/admin/shop_attribute/*` przepiety z legacy `grid/gridEdit` na `components/table-list`, `components/form-edit` oraz nowy edytor wartosci (`values-edit`)
- UPDATE - routing i menu admin przepiete na kanoniczny URL `/admin/shop_attribute/list/` (bez aliasow legacy)
- UPDATE - przepiecie zaleznosci kombinacji produktu: `admin\controls\ShopProduct`, `admin\factory\ShopProduct`, `admin/templates/shop-product/product-combination.php`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopAttribute.php`, `autoload/admin/factory/class.ShopAttribute.php`, `autoload/admin/view/class.ShopAttribute.php`, `admin/templates/shop-attribute/_partials/value.php`
- UPDATE - testy: `OK (312 tests, 948 assertions)` + nowe pliki testowe `AttributeRepositoryTest`, `ShopAttributeControllerTest`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.271.zip`, `ver_0.271_files.txt`
<hr>
<b>ver. 0.270 - 14.02.2026</b><br />
- FIX - Apilo: `shop\Order::set_as_paid()` wysyla mapowany typ platnosci Apilo (z `payment_method_id`), zamiast stalego `type = 1`
- NEW - Apilo: dodana kolejka retry `temp/apilo-sync-queue.json` dla nieudanych syncow platnosci/statusu (chwilowa niedostepnosc API)
- UPDATE - `cron.php`: automatyczne ponawianie zaleglych syncow przez `Order::process_apilo_sync_queue(10)`
- UPDATE - debug Apilo: rozszerzone logi odpowiedzi o HTTP code i bledy cURL dla sync platnosci/statusu
- UPDATE - testy: `OK (300 tests, 895 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.270.zip`, `ver_0.270_files.txt`
<hr>
<b>ver. 0.269 - 14.02.2026</b><br />
- NEW - migracja modulu `ShopPaymentMethod` do architektury Domain + DI (`Domain\PaymentMethod\PaymentMethodRepository`, `admin\Controllers\ShopPaymentMethodController`)
- UPDATE - modul `/admin/shop_payment_method/*` przepiety z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit` (nowe widoki listy i edycji)
- UPDATE - przepiecie zaleznosci na nowe repozytorium: `admin\controls\ShopTransport`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopPaymentMethod.php`, `autoload/admin/factory/class.ShopPaymentMethod.php`, `autoload/admin/view/class.ShopPaymentMethod.php`, `admin/templates/shop-payment-method/view-list.php`
- UPDATE - Apilo: dodane automatyczne odswiezanie tokenu przed wygasnieciem (`apiloKeepalive`) oraz bardziej szczegolowe komunikaty bledow integracji
- UPDATE - testy: `OK (280 tests, 828 assertions)` + nowe pliki testowe `PaymentMethodRepositoryTest`, `ShopPaymentMethodControllerTest`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.269.zip`, `ver_0.269_files.txt`
<hr>
<b>ver. 0.268 - 14.02.2026</b><br />
- NEW - migracja modulu `ShopStatuses` do architektury Domain + DI (`Domain\ShopStatus\ShopStatusRepository`, `admin\Controllers\ShopStatusesController`)
- UPDATE - modul `/admin/shop_statuses/*` przepiety z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`
- NEW - nowy typ pola formularza `color` (HTML5 color picker + pole tekstowe zsynchronizowane)
- UPDATE - `front\factory\ShopStatuses` dziala jako fasada do `Domain\ShopStatus\ShopStatusRepository`
- UPDATE - menu admin przepiete na kanoniczny URL `/admin/shop_statuses/list/`
- CLEANUP - usuniete legacy klasy: `autoload/admin/controls/class.ShopStatuses.php`, `autoload/admin/factory/class.ShopStatuses.php`
- UPDATE - reorganizacja dokumentacji technicznej: pliki przeniesione do folderu `docs/` i rozbite na mniejsze pliki tematyczne
- UPDATE - testy: `OK (254 tests, 736 assertions)` + nowe pliki testowe `ShopStatusRepositoryTest`, `ShopStatusesControllerTest`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.268.zip`, `ver_0.268_files.txt`
<hr>
<b>ver. 0.267 - 13.02.2026</b><br />
- FIX - front: poprawione dobieranie layoutu dla kategorii/produktu/koszyka i innych stron moduĹowych (fallback do layoutu domyĹlnego)
- FIX - produkt/koszyk: poprawiona obsĹuga iloĹci dla kombinacji (stan 0 po dodaniu do koszyka, limit max, odczyt `stock_0_buy`)
- FIX - produkt: usunięty ąd JS `TypeError: $(...).visible is not a function` (zamiana na `:visible`)
- FIX - SEO redirecty produktĂłw: blokada konfliktĂłw po kopiowaniu URL oraz utwardzone wykrywanie pÄ™tli redirectĂłw (`lang_id` + graf przejĹć)
- UPDATE - admin: `input-switch` zapisuje wartoĹć `on` (spĂłjnie z obsĹugÄ… pĂłl checkbox w formularzach)
- CLEANUP - usunięte pliki: `apilo-bck`, `geocode-cache.php`
- UPDATE - testy: `OK (235 tests, 682 assertions)`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.267.zip`, `ver_0.267_files.txt`, `ver_0.267_sql.txt`
<hr>
<b>ver. 0.266 - 13.02.2026</b><br />
- NEW - migracja modulu `ShopCoupon` do architektury Domain + DI (`Domain\Coupon\CouponRepository`, `admin\Controllers\ShopCouponController`)
- UPDATE - modul `/admin/shop_coupon/*` przepiety z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`
- UPDATE - nowe widoki i partiale: `shop-coupon/coupons-list`, `shop-coupon/coupon-edit-new`, `shop-coupon/coupon-categories-selector`, `shop-coupon/coupon-categories-tree`, `shop-coupon/coupon-edit-custom-script`
- UPDATE - zachowana kompatybilnosc aliasow legacy akcji (`view_list`, `coupon_edit`, `coupon_save`, `coupon_delete`) w nowym kontrolerze
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopCoupon.php`, `autoload/admin/factory/class.ShopCoupon.php`, `admin/templates/shop-coupon/view-list.php`, `admin/templates/shop-coupon/coupon-edit.php`
- UPDATE - menu admin wskazuje kanoniczny URL `/admin/shop_coupon/list/`
- FIX - ujednolicone drzewka (strzalki + focus) i wyglad checkboxow miedzy `/admin/shop_coupon/edit/*` oraz `/admin/layouts/edit/*`
- UPDATE - testy: `OK (235 tests, 682 assertions)` + nowe pliki testowe `CouponRepositoryTest`, `ShopCouponControllerTest`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.266.zip`, `ver_0.266_files.txt`
<hr>
<b>ver. 0.265 - 13.02.2026</b><br />
- UPDATE - modul `/admin/shop_promotion/*`: dodano pole `Data od` (`date_from`) w repozytorium, formularzu i liscie
- UPDATE - front: `shop\Promotion::get_active_promotions()` uwzglednia `date_from` (okno aktywnosci od-do)
- FIX - edycja promocji zapisuje aktualizacje rekordu zamiast tworzenia nowego (`id` przekazywane przez hidden field + fallback z URL)
- UPDATE - testy: `OK (222 tests, 614 assertions)`
<hr><b>ver. 0.264 - 13.02.2026</b><br />
- NEW - migracja modulu `ShopPromotion` do architektury Domain + DI (`Domain\Promotion\PromotionRepository`, `admin\Controllers\ShopPromotionController`)
- UPDATE - modul `/admin/shop_promotion/*` przepiety z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`
- UPDATE - nowe widoki i partiale: `shop-promotion/promotions-list`, `shop-promotion/promotion-edit`, `shop-promotion/promotion-categories-selector`, `shop-promotion/promotion-categories-tree`, `shop-promotion/promotion-edit-custom-script`
- CLEANUP - usuniete legacy klasy/pliki: `autoload/admin/controls/class.ShopPromotion.php`, `autoload/admin/factory/class.ShopPromotion.php`, `admin/templates/shop-promotion/view-list.php`
- UPDATE - menu admin wskazuje kanoniczny URL `/admin/shop_promotion/list/`
- UPDATE - testy: `OK (222 tests, 609 assertions)` + nowe pliki testowe `PromotionRepositoryTest`, `ShopPromotionControllerTest`
<hr><b>ver. 0.263 - 13.02.2026</b><br />
- NEW - migracja modulu `Integrations` do architektury Domain + DI (`Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`)
- CLEANUP - usunieto integracje Sellasist i Baselinker z calego projektu (kontrolery, factory, szablony, referencje w cron/Order/ShopStatuses/ShopTransport/ShopPaymentMethod/ShopProduct)
- UPDATE - `admin\factory\Integrations` jako fasada delegujaca do repozytorium (tylko Apilo + ShopPRO)
- FIX - naprawione polskie znaki w `product-edit.php` (usuniety podwojny encoding UTF-8/CP1250)
- CLEANUP - usuniete pliki: `controls/Integrations`, `controls/Baselinker`, `factory/Baselinker`, `front/factory/Shop`, `shop/ShopStatus`, szablony sellasist/baselinker
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.263.zip`, `ver_0.263_files.txt`
<hr><b>ver. 0.262 - 13.02.2026</b><br />
- NEW - migracja modulu `Pages` do architektury Domain + DI (`Domain\\Pages\\PagesRepository`, `admin\\Controllers\\PagesController`)
- UPDATE - widoki `/admin/pages/*` przepiete na nowy routing i komponent `components/form-edit` (menu/page edit)
- FIX - przywrocony przycisk generowania linku SEO w edycji strony (zakladka SEO, pola jezykowe)
- FIX - popup potwierdzenia usuwania menu/strony ujednolicony z `table-list-confirm-dialog` + poprawione polskie znaki
- CLEANUP - usuniete legacy pliki Pages: `admin/ajax/pages.php`, `autoload/admin/controls/class.Pages.php`, `autoload/admin/factory/class.Pages.php`, `autoload/admin/view/class.Pages.php`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.262.zip`, `ver_0.262_files.txt`
<hr><b>ver. 0.261 - 13.02.2026</b><br />
- UPDATE - finalizacja refaktoryzacji modulu `Articles` (`/admin/articles`) w warstwie Domain + DI
- UPDATE - nowe akcje AJAX w `admin\Controllers\ArticlesController` (m.in. `files_order_save`)
- UPDATE - sortowanie zalacznikow i zdjec w edycji artykulu (drag&drop + zapis kolejnosci przy pierwszym zapisie)
- UPDATE - utwardzenie uploadow (wspolny helper `libraries/plupload/upload-common.php`, walidacje i tokeny)
- FIX - potwierdzenia usuwania zdjec/zalacznikow ujednolicone z widokiem listy (jquery-confirm)
- CLEANUP - usuniete legacy `admin/ajax/articles.php` i `autoload/admin/view/class.Articles.php`
- UPDATE - pliki aktualizacji: `updates/0.20/ver_0.261.zip`, `ver_0.261_files.txt`, `ver_0.261_sql.txt`
<hr><b>ver. 0.260 - 12.02.2026</b><br />
- NEW - migracja modulu `ArticlesArchive` do architektury Domain + DI (`admin\\Controllers\\ArticlesArchiveController`)
- UPDATE - `Domain\\Article\\ArticleRepository` rozszerzone o metody `listArchivedForAdmin`, `restore`, `deletePermanently`
- UPDATE - widok `/admin/articles_archive/view_list/` przepiety z legacy `grid` na `components/table-list`
- UPDATE - routing DI (`admin\\Site`) rozszerzony o modul `ArticlesArchive` + mapowanie akcji `article_restore -> restore`
- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.ArticlesArchive.php`, `autoload/admin/factory/class.ArticlesArchive.php`, `autoload/admin/view/class.ArticlesArchive.php`
- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.260_files.txt`
<hr><b>ver. 0.259 - 12.02.2026</b><br />
- NEW - migracja modulu `Scontainers` do architektury Domain + DI (`Domain\\Scontainers\\ScontainersRepository`, `admin\\Controllers\\ScontainersController`)
- UPDATE - widoki `/admin/scontainers/*` przepiete z legacy `grid/gridEdit` na `components/table-list` i `components/form-edit`
- UPDATE - routing DI (`admin\\Site`) rozszerzony o modul `Scontainers` + mapowanie akcji `container_edit/container_save/container_delete`
- UPDATE - `admin\\factory\\Scontainers` dziala jako fasada do repozytorium (backward compatibility)
- UPDATE - `front\\factory\\Scontainers` korzysta z `Domain\\Scontainers\\ScontainersRepository`
- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.Scontainers.php`, `autoload/admin/view/class.Scontainers.php`
- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.259_files.txt`
<hr><b>ver. 0.258 - 12.02.2026</b><br />
- UPDATE - modul `Newsletter`: funkcjonalnosc `Wysylka - przygotowanie` zostala tymczasowo wylaczona (menu + akcje `prepare/send/preview`)
- UPDATE - modul `Newsletter`: lista `Szablony uzytkownika` zostala tymczasowo wylaczona (menu + akcja `email_templates_user`)
- UPDATE - `NewsletterController`: lista szablonow ograniczona do szablonow administracyjnych (`is_admin = 1`)
- UPDATE - `email_template_edit` i `template_save` obsluguja tylko szablony administracyjne
- CLEANUP - usuniete nieuzywane szablony newslettera: `admin/templates/newsletter/prepare.php`, `admin/templates/newsletter/preview.php`, `admin/templates/newsletter/email-templates-user.php`
- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.258_files.txt`
<hr><b>ver. 0.257 - 12.02.2026</b><br />
- NEW - migracja modulu `Newsletter` do architektury Domain + DI (`Domain\\Newsletter\\NewsletterRepository`, `Domain\\Newsletter\\NewsletterPreviewRenderer`, `admin\\Controllers\\NewsletterController`)
- UPDATE - widoki `/admin/newsletter/*` przepiete z legacy `grid/gridEdit` na nowe komponenty (`components/table-list`, `components/form-edit`) + nowy endpoint `/admin/newsletter/preview/`
- UPDATE - routing DI (`admin\\Site`) rozszerzony o moduĹ `Newsletter`
- UPDATE - `admin\\factory\\Newsletter` dziala jako fasada do nowego repozytorium (backward compatibility)
- UPDATE - `front\\factory\\Newsletter` nie korzysta juz z `admin\\view\\Newsletter`
- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.Newsletter.php`, `autoload/admin/view/class.Newsletter.php`
<hr><b>ver. 0.256 - 12.02.2026</b><br />
- NEW - migracja modulu `Layouts` do architektury Domain + DI (`Domain\\Layouts\\LayoutsRepository`, `admin\\Controllers\\LayoutsController`)
- UPDATE - lista `/admin/layouts/view_list/` przepieta z legacy `grid` na `components/table-list` (filtry, sortowanie, paginacja)
- UPDATE - `layouts/layout-edit` korzysta z danych z repozytorium (menus/categories), bez wywolan legacy factory w widoku
- UPDATE - `Domain\\Languages\\LanguagesRepository` rozszerzone o wspolna metode `defaultLanguageId()`
- UPDATE - `admin\\Controllers\\ArticlesController` pobiera layouty przez `Domain\\Layouts\\LayoutsRepository` (DI)
- CLEANUP - usuniete legacy klasy `autoload/admin/controls/class.Layouts.php`, `autoload/admin/view/class.Layouts.php`
<hr><b>ver. 0.255 - 12.02.2026</b><br />
- UPDATE - kontrolery admin `Settings`, `Banners`, `Dictionaries`, `Articles` pobieraja liste jezykow przez `Domain\\Languages\\LanguagesRepository` (DI)
- UPDATE - routing DI (`admin\\Site`) przekazuje `LanguagesRepository` do kontrolerow `Articles`, `Banners`, `Settings`, `Dictionaries`
- UPDATE - aktywne legacy odwolania (`admin\\controls`, `admin\\factory\\Shop*`) przepiete z `admin\\factory\\Languages` na `LanguagesRepository`
- FIX - `autoload/admin/factory/class.Languages.php` uzywa pelnego znacznika PHP (zgodnosc z `short_open_tag=Off`)
<hr><b>ver. 0.254 - 12.02.2026</b><br />
- UPDATE - modul `Languages` w panelu admin przepiety na `Domain\\Languages\\LanguagesRepository` + `admin\\Controllers\\LanguagesController`
- UPDATE - migracja widokow languages (`languages-list`, `language-edit`, `translations-list`, `translation-edit`) na `components/table-list` i `components/form-edit`
- UPDATE - routing DI dla `Languages` w `admin\\Site` oraz kompatybilna fasada `admin\\factory\\Languages` delegujaca do repozytorium
- UPDATE - naprawiono zapis edycji jezyka (ID jezyka pobierane z URL przy edycji)
- UPDATE - globalne poprawki UX filtrĂłw w `components/table-list` (kompaktowe kolumny `Aktywny`/`Domyslny`, spacing i pelna szerokosc selecta)
- CLEANUP - usuniete legacy klasy: `autoload/admin/controls/class.Languages.php`, `autoload/admin/view/class.Languages.php`
<hr>
<b>ver. 0.253 - 12.02.2026</b><br />
- UPDATE - modul `Users` w panelu admin w pelni przepiety na `Domain\\User\\UserRepository` + `admin\\Controllers\\UsersController`
- UPDATE - migracja widokow users z `grid/gridEdit` na nowe komponenty (`components/table-list`, `components/form-edit`)
- UPDATE - dodana walidacja warunkowa: przy wlaczonym 2FA pole `E-mail do 2FA` jest wymagane
- UPDATE - globalne ulepszenia `components/table-list` (kompaktowe filtry select/status i odstepy w formularzu paginacji)
- CLEANUP - usuniete legacy klasy users: `autoload/admin/controls/class.Users.php`, `autoload/admin/factory/class.Users.php`, `autoload/admin/view/class.Users.php`
<hr>
<b>ver. 0.252 - 10.02.2026</b><br />
- UPDATE - migracja listy archiwum produktow do nowego komponentu tabeli (`components/table-list`) z filtrowaniem i paginacja
- UPDATE - banery i archiwum produktow: wydzielenie CSS/JS do osobnych widokow `*-custom-script.php`
- UPDATE - filemanager przepiety na nowy routing (`admin\\Controllers\\FilemanagerController`)
- FIX - naprawiono blad `Invalid Key` w widoku filemanagera po refaktoryzacji
- UPDATE - usunieto legacy klasy i stare szablony (`admin\\controls`, `admin\\view`, `admin/templates/product_archive`)
<hr>
<b>ver. 0.251 - 09.02.2026</b><br />
- NEW - migracja modulu Dictionaries do nowej architektury (Domain + admin Controller + DI)
- UPDATE - lista i formularz Dictionaries przepiete na nowe komponenty (`components/table-list`, `components/form-edit`)
- UPDATE - dodano globalne ograniczenie szerokosci pierwszej kolumny (Lp.) w `components/table-list`
- FIX - zapis tlumaczen jednostek obsluguje `lang_id` jako string (`pl`, `en`)
- UPDATE - usunieto legacy klasy Dictionaries: `admin\\controls`, `admin\\factory`, `front\\factory`
- UPDATE - przepieto uzycia na `Domain\\Dictionaries\\DictionariesRepository` (`shop-product`, `shop_product` admin)
<hr>
<b>ver. 0.250</b><br />
- UPDATE - refaktoryzacja Settings: `Domain\\Settings\\SettingsRepository` ma bezposredni dostep do bazy (bez delegacji do `admin\\factory\\Settings`)
- UPDATE - przepieto pozostale uzycia `admin\\factory\\Settings` na `Domain\\Settings\\SettingsRepository` (`admin\\controls\\Settings`, `admin\\controls\\Newsletter`, `front\\factory\\Newsletter`)
- UPDATE - DI dla SettingsController: repozytorium otrzymuje `$mdb` w `admin\\Site`
- UPDATE - Settings: widok edycji przeniesiony na nowy mechanizm formularza (`FormEditViewModel` + `components/form-edit`) jak w banerach
- UPDATE - usunieto nieuzywana legacy klase `autoload/admin/factory/class.Settings.php`
- UPDATE - usunieto legacy fallback kontrolera `autoload/admin/controls/class.Settings.php`
- UPDATE - usunieto nieuzywana klase widoku `autoload/admin/view/class.Settings.php`
<hr><b>ver. 0.249</b><br />
- FIX - banner edit: poprawiono zapisywanie danych jezykowych i synchronizacje CKEditor przed zapisem
- FIX - banner edit: naprawiono hash zakladek (usunieto `undefined` w URL)
- FIX - filemanager: przywrocono dzialanie popupa wyboru obrazka z banera
- UPDATE - komunikaty zapisu w nowym formularzu sa wyswietlane w stylu panelu (bez natywnego alertu JS)
- UPDATE - lista banerow: dodano kolumne miniatury oraz podglad duzego obrazka w popup po najechaniu
- UPDATE - usunieto nieuzywane legacy klasy banerow: `admin\\view\\Banners`, `admin\\factory\\Banners`
<hr><b>ver. 0.248</b><br />
- UPDATE - filtry w nowych tabelach dzialaja automatycznie na `onchange`
- UPDATE - `components/table-list`: auto-submit formularza filtrow po zmianie pola (select, date, text)
<hr><b>ver. 0.247</b><br />
- UPDATE - nowy dialog potwierdzenia usuwania w `components/table-list` (zamiast natywnego `confirm`)
- UPDATE - popup usuwania: wiekszy rozmiar i centrowanie na srodku ekranu
<hr><b>ver. 0.246</b><br />
- UPDATE - migracja listy banerow do nowego mechanizmu tabeli (`components/table-list`, filtrowanie, sortowanie, paginacja)
- UPDATE - `admin\Controllers\BannerController::list()` buduje `PaginatedTableViewModel`
- UPDATE - `Domain\Banner\BannerRepository::listForAdmin()` (bezpieczne filtrowanie i sortowanie)
- UPDATE - usunieto legacy kontroler `autoload/admin/controls/class.Banners.php`
- UPDATE - plik do usuniecia dodany w `updates/0.20/ver_0.246_files.txt`
<hr><b>ver. 0.245</b><br />
- UPDATE - refaktoryzacja listy artykulow: wspolny komponent `admin/templates/components/table-list.php` + `PaginatedTableViewModel`
- NEW - `admin\Support\TableListRequestFactory` (wspolna obsluga filtrow, sortowania i paginacji dla list)
- UPDATE - `Domain\Article\ArticleRepository::listForAdmin()` utwardzone pod katem bezpieczenstwa (whitelist sortowania, bind params, limit per_page)
- UPDATE - usunieto legacy `browse_list` dla modulu Articles
- UPDATE - usuniete pliki legacy sa wyszczegolnione w `updates/0.20/ver_0.245_files.txt`
- FIX - generator `.htaccess` i `libraries/htaccess.conf` (QSA dla `/admin/...`, komentarz niedozwolonych dyrektyw `SetHandler/AddHandler/ForceType`)
<hr><b>ver. 0.244</b><br />
- UPDATE - refaktoryzacja: article_save przeniesiony do Domain\Article\ArticleRepository::save() z prywatnymi helperami
- UPDATE - refaktoryzacja: article_delete przeniesiony do Domain\Article\ArticleRepository::archive()
- UPDATE - ArticlesController: nowe akcje save() i delete() z DI
- UPDATE - admin\factory\Articles::article_save() i articles_set_archive() delegujÄ… do repozytorium (kompatybilnoĹć)
<hr><b>ver. 0.243</b><br />
- UPDATE - refaktoryzacja: cleanup nieprzypisanych plikĂłw/zdjęć artykuĹĂłw przeniesiony do Domain\Article\ArticleRepository
- UPDATE - ArticlesController::edit() uĹĽywa repozytorium do cleanupu, a admin\factory\Articles zachowuje delegowanie (kompatybilnoĹć)
<hr><b>ver. 0.242</b><br />
- NEW - refaktoryzacja: Domain\Article\ArticleRepository + migracja article_edit do admin\Controllers\ArticlesController (DI)
- UPDATE - admin\factory\Articles::article_details() deleguje do nowego repozytorium (kompatybilnoĹć zachowana)
- UPDATE - metody przejęte przez nowe kontrolery oznaczone jako @deprecated w legacy kontrolerach admin\controls
<hr><b>ver. 0.241</b><br />
- NEW - refaktoryzacja: admin\Controllers\ProductArchiveController - archiwum produktĂłw z DI
- NEW - ProductRepository::archive(), unarchive() - operacje archiwizacji w repozytorium
- FIX - naprawiono SQL w liĹcie archiwum (puste wyszukiwanie filtrowaĹo wszystkie wyniki)
- FIX - naprawiono brakujÄ…cy filtr archive = 1 w zapytaniu bez wyszukiwania
- UPDATE - wyczyszczono szablony archiwum (usunięto zbędne funkcje: apilo, baselinker, duplikowanie)
<hr>
<b>ver. 0.240</b><br />
- NEW - refaktoryzacja: Domain\Settings\SettingsRepository + admin\Controllers\SettingsController (architektura Domain-Driven)
- NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsĹugÄ… Redis
- FIX - komunikat potwierdzenia zapisu ustawień w panelu administratora
- FIX - naprawiono element #content w layoucie admina (powiadomienia grid.js)
<hr><b>ver. 0.239</b><br />
- NEW - refaktoryzacja: Domain\Banner\BannerRepository + admin\Controllers\BannerController (peĹna migracja kontrolera)
- NEW - refaktoryzacja: Domain\Product\ProductRepository::getPrice(), getName() - migracja kolejnych metod
- NEW - router admin z obsĹugÄ… nowych kontrolerĂłw (fallback na stare)
- UPDATE - shop\Product::get_product_price(), get_product_name() uĹĽywajÄ… nowego repozytorium (kompatybilnoĹć zachowana)
<hr>
<b>ver. 0.238</b><br />
- NEW - refaktoryzacja: Domain\Product\ProductRepository - pierwsza klasa w nowej architekturze Domain-Driven
- NEW - Dependency Injection zamiast global variables
- UPDATE - shop\Product::get_product_quantity() uĹĽywa teraz nowego repozytorium (kompatybilnoĹć zachowana)
<hr>
<b>ver. 0.237</b><br />
- NEW - automatyczne czyszczenie cache produktu po aktualizacji przez CRON (Sellasist, Apilo, Baselinker)
- UPDATE - przycisk "WyczyĹć cache" w panelu administratora z obsĹugÄ… AJAX i komunikatami o postÄ™pie
<hr>
<b>ver. 0.236</b><br />
- FIX - zabezpieczenie przed duplikatami zamówień w Apilo - automatyczne pobieranie ID zamówienia przy ędzie "idExternal już wykorzystywany"
<hr>
<b>ver. 0.235</b><br />
- FIX - poprawka funkcji aktualizacji
<hr>
<b>ver. 0.234</b><br />
- NEW - przycisk zaznaczania zamĂłwienia jako wysĹane do trustmate.io
<hr>
<b>ver. 0.232</b><br />
- NEW - opcje GPSR
<hr>
<b>ver. 0.231</b><br />
- FIX - poprawki bezpieczeństwa + dwuetapowa weryfikacja logowania
<hr>
<b>ver. 0.230</b><br />
- FIX - poprawki bezpieczeństwa
<hr>
<b>ver. 0.229</b><br />
- NEW - pola dodatkowe z opcjÄ… wymagane/niewymagane
<hr>
<b>ver. 0.228</b><br />
- NEW - cron do wysyĹania zamĂłwieĹ„ do trustmate.io
<hr>
<b>ver. 0.227</b><br />
- NEW - historia kodĂłw rabatowych
<hr>
<b>ver. 0.226</b><br />
- NEW - dodanie opcji faktury do zamĂłwienia
<hr>
<b>ver. 0.225</b><br />
- NEW - przycisk czyszczenia cache<br>
- NEW - ponowne wysyĹanie zamĂłwienia do apilo
<hr>
<b>ver. 0.224</b><br />
- NEW - sortowanie form dostawy
<hr>
<b>ver. 0.223</b><br />
- FIX - integracja z Orlen Paczka
<hr>
<b>ver. 0.222</b><br />
- NEW - integracja z Orlen Paczka
<hr>
<b>ver. 0.221</b><br />
- NEW - Automatyczne przekierowania adresĂłw URL produktĂłw, zmiany w pliku htaccess
<hr>
<b>ver. 0.220</b><br />
- NEW - Dodanie moĹĽliwoĹci wyĹwietlenia na strone ostatnio dodane produkty [PRODUKTY_NEW] lub [PRODUKTY_NEW:10].<br>
- NEW - Dodanie moĹĽliwoĹci wyĹwietlenia na strone popularnych produktĂłw [PRODUKTY_TOP] lub [PRODUKTY_TOP:10].
<hr>
<b>ver. 0.219</b><br />
- NEW - Dodanie moĹĽliwoĹci zmiany daty w artykuĹach
<hr>
<b>ver. 0.218</b><br />
- NEW - indywidualny kod GTM
<hr>
<b>ver. 0.217</b><br />
- NEW - zwiÄ™kszenie obsĹugi REDIS
<hr>
<b>ver. 0.216</b><br />
- NEW - aktualizacja api i cron (apilo)
<hr>
<b>ver. 0.215</b><br />
- FIX - generowanie pliku .htaccess
<hr>
<b>ver. 0.214</b><br />
- NEW - dodanie API
<hr>
<b>ver. 0.213</b><br />
- FIX - wyliczenie darmowej dostawy
<hr>
<b>ver. 0.212</b><br />
- NEW - zmiany w zapisywaniu zamĂłwienia do apilo
<hr>
<b>ver. 0.211</b><br />
- NEW - Debugowanie apilo + wyĹwietlanie podkategorii
<hr>
<b>ver. 0.210</b><br />
- NEW - dodatkowe pola w widoku produktĂłw
<hr>
<b>ver. 0.209</b><br />
- NEW - zmiany w widoku produktĂłw (panel administratora)
<hr>
<b>ver. 0.208</b><br />
- NEW - zmiany w wyszukiwarce produktĂłw
<hr>
<b>ver. 0.204-0.207</b><br />
- NEW - htaccess update
<hr>
<b>ver. 0.204-0.206</b><br />
- NEW - wysyĹanie produktĂłw do apilo
<hr>
<b>ver. 0.203</b><br />
- NEW - zmiana sposobu wyliczania cen produkty z dodatkami
<hr>
<b>ver. 0.202</b><br />
- NEW - dodano "Ăłwne zdjÄ™cie" w edycji artykuĹu
<hr>
<b>ver. 0.201</b><br />
- FIX - aktualizacja statusĂłw na podstawie baselinkera
<hr>
<b>ver. 0.200</b><br />
- NEW - wysyĹanie produktĂłw do baselinker
<hr>
<b>ver. 0.199</b><br />
- NEW - usprawnienie edycji danych do XML
<hr>
<b>ver. 0.198</b><br />
- NEW - automatyczne generowanie kodĂłw SKU
<hr>
<b>ver. 0.197</b><br />
- FIX - poprawki w Dashboard
<hr>
<b>ver. 0.196</b><br />
- FIX - integracja z apilo.com
<hr>
<b>ver. 0.195</b><br />
- FIX - aktualizacja statusĂłw
<hr>
<b>ver. 0.194</b><br />
- UPDATE - integracja apilo
<hr>
<b>ver. 0.193</b><br />
- UPDATE - aktualizacja synchronizacji z baselinker
<hr>
<b>ver. 0.192</b><br />
- NEW - pobieranie statusĂłw z sellasist
<hr>
<b>ver. 0.191</b><br />
- NEW - integracja z selasist
<hr>
<b>ver. 0.190</b><br />
- FIX - produkty powiÄ…zane
<hr>
<b>ver. 0.189</b><br />
- FIX - ceny promocyjne produktĂłw z dodatkiem
<hr>
<b>ver. 0.188</b><br />
- NEW - widok listy produktĂłw
<hr>
<b>ver. 0.187</b><br />
- FIX - pobieranie cen z APILO
<hr>
<b>ver. 0.186</b><br />
- FIX - dodawanie do koszyka tych samych produktów ale z różną personalizacją
<hr>
<b>ver. 0.185</b><br />
- FIX - masowa edycja produktĂłw
<hr>
<b>ver. 0.184</b><br />
- NEW - druga czÄ™Ĺć integracji z apilo, masowa edycja produktĂłw
<hr>
<b>ver. 0.183</b><br />
- NEW - pierwsza czÄ™Ĺć integracji z apilo
<hr>
<b>ver. 0.182</b><br />
- FIX - layout
<hr>
<b>ver. 0.181</b><br />
- NEW - infinitescroll - opcja Ä…czy/wyĹÄ…cz
<hr>
<b>ver. 0.180</b><br />
- NEW - aktualizacja dashboard
<hr>
<b>ver. 0.179</b><br />
- NEW - obsĹuga EAN
<hr>
<b>ver. 0.177, 0.178</b><br />
- FIX - custom_label
<hr>
<b>ver. 0.176</b><br />
- NEW - custom_label
<hr>
<b>ver. 0.175</b><br />
- NEW - nowe statystyki
<hr>
<b>ver. 0.174</b><br />
- FIX - generowanie xml
<hr>
<b>ver. 0.173</b><br />
- NEW - duplikowanie produktu wraz z kombinacjami
- NEW - dodanie przechodzenia pomiędzy zamówienia (poprzednie/następne zamówienie)
<hr>
<b>ver. 0.172</b><br />
- FIX - poprawki w Cache
<hr>
<b>ver. 0.171</b><br />
- FIX - poprawki w Cache
<hr>
<b>ver. 0.170</b><br />
- NEW - usuwanie cache produktu przy zapisie
<hr>
<b>ver. 0.169</b><br />
- FIX - poprawki w liĹcie produktĂłw
<hr>
<b>ver. 0.168</b><br />
- NEW - archiwum produktĂłw
<hr>
<b>ver. 0.167</b><br />
- NEW - dodanie obsĹugi cen i stanĂłw magazynowych kombinacji produktĂłw
<hr>
<b>ver. 0.166</b><br />
- NEW - wspĂłĹpraca z GTM
<hr>
<b>ver. 0.164/5</b><br />
- FIX - ukrywanie produktĂłw nieaktywnych
<hr>
<b>ver. 0.163</b><br />
- NEW - automatyczne podpowiadanie produktĂłw do zestawu na podstawie wczeĹniejszych zakupĂłw klientĂłw
<hr>
<b>ver. 0.162</b><br />
- NEW - GA4
<hr>
<b>ver. 0.161</b><br />
- UPDATE - aktualizacja menu administratora
<hr>
<b>ver. 0.160</b><br />
- UPDATE - aktualizacja cron Baselinker
- NEW - waga i cena jednostkowa produktu
<hr>
<b>ver. 0.159</b><br />
- FIX - cron Baselinker
<hr>
<b>ver. 0.158</b><br />
- UPDATE - poprawa kolorystyki przyciskĂłw
<hr>
<b>ver. 0.157</b><br />
- NEW - szybka zmiana statusu produktu
<hr>
<b>ver. 0.156</b><br />
- NEW - dodanie szybkiej edycji google xml label
<hr>
<b>ver. 0.155</b><br />
- NEW - infinite scroll w widoku kategorii
<hr>
<b>ver. 0.154</b><br />
- FIX - atrybuty produktĂłw
<hr>
<b>ver. 0.153</b><br />
- FIX - atrybuty produktĂłw
<hr>
<b>ver. 0.152</b><br />
- FIX - tematy maili
<hr>
<b>ver. 0.151</b><br />
- FIX - tematy maili
<hr>
<b>ver. 0.150</b><br />
- NEW - domyĹlna forma transportu
<hr>
<b>ver. 0.149</b><br />
- NEW - tematy maili
<hr>
<b>ver. 0.148</b><br />
- FIX - cron-xml
<hr>
<b>ver. 0.147</b><br />
- FIX - cron-xml
<hr>
<b>ver. 0.146</b><br />
- NEW - cron-xml
<hr>
<b>ver. 0.145</b><br />
- NEW - omnibus ready
<hr>
<b>ver. 0.144</b><br />
- FIX - usunięcie adresu marianek.pl z kodu
<hr>
<b>ver. 0.143</b><br />
- FIX - poprawa generowania plikĂłw WEBP
<hr>
<b>ver. 0.142</b><br />
- FIX - poprawa adresu strony Ăłwnej
<?php
// Auto-generated changelog from manifest files + legacy entries.
// Scans manifest JSON files, merges with changelog-legacy.json, sorts descending, outputs HTML.
$entries = [];
// 1. Scan manifest files
$manifests = glob( __DIR__ . '/0.*/ver_*_manifest.json' );
if ( $manifests ) {
foreach ( $manifests as $file ) {
$json = @file_get_contents( $file );
if ( !$json ) continue;
// Strip UTF-8 BOM if present
if ( substr( $json, 0, 3 ) === "\xEF\xBB\xBF" ) {
$json = substr( $json, 3 );
}
$data = @json_decode( $json, true );
if ( !$data || empty( $data['version'] ) || empty( $data['changelog'] ) ) continue;
$date = isset( $data['date'] ) ? $data['date'] : '';
// Convert YYYY-MM-DD to DD.MM.YYYY
if ( $date && preg_match( '/^(\d{4})-(\d{2})-(\d{2})$/', $date, $m ) ) {
$date = $m[3] . '.' . $m[2] . '.' . $m[1];
}
$entries[] = [
'version' => (float) $data['version'],
'ver_str' => $data['version'],
'date' => $date,
'text' => $data['changelog'],
];
}
}
// 2. Load legacy entries
$legacyFile = __DIR__ . '/changelog-legacy.json';
if ( file_exists( $legacyFile ) ) {
$legacyJson = @file_get_contents( $legacyFile );
if ( $legacyJson ) {
$legacy = @json_decode( $legacyJson, true );
if ( is_array( $legacy ) ) {
foreach ( $legacy as $item ) {
if ( empty( $item['version'] ) ) continue;
$entries[] = [
'version' => (float) $item['version'],
'ver_str' => $item['version'],
'date' => isset( $item['date'] ) ? $item['date'] : '',
'text' => isset( $item['text'] ) ? $item['text'] : '',
];
}
}
}
}
// 3. Sort descending by version
usort( $entries, function ( $a, $b ) {
if ( $a['version'] == $b['version'] ) return 0;
return ( $a['version'] > $b['version'] ) ? -1 : 1;
} );
// 4. Output HTML
foreach ( $entries as $entry ) {
$header = 'ver. ' . htmlspecialchars( $entry['ver_str'] );
if ( $entry['date'] ) {
$header .= ' - ' . htmlspecialchars( $entry['date'] );
}
$text = nl2br( htmlspecialchars( $entry['text'] ) );
echo '<b>' . $header . '</b><br />' . "\n";
echo $text . "\n";
echo '<hr>' . "\n";
}

View File

@@ -1,5 +1,5 @@
<?
$current_ver = 302;
<?
$current_ver = 307;
for ($i = 1; $i <= $current_ver; $i++)
{