Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f16f5ce8f8 | |||
| 2dc1360395 | |||
| 5c1842181a | |||
| 62255541ab | |||
| ec77160130 |
@@ -31,6 +31,13 @@ config.php
|
|||||||
admin/.htaccess
|
admin/.htaccess
|
||||||
libraries/version.ini
|
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 / cache / backups
|
||||||
temp/
|
temp/
|
||||||
backups/
|
backups/
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ composer test
|
|||||||
|
|
||||||
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpunit.xml`.
|
||||||
|
|
||||||
Current suite: **734 tests, 2080 assertions**.
|
Current suite: **739 tests, 2089 assertions**.
|
||||||
|
|
||||||
### Creating Updates
|
### 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.
|
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.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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,
|
#images-uploader,
|
||||||
#files-uploader {
|
#files-uploader {
|
||||||
clear: both;
|
clear: both;
|
||||||
|
|||||||
@@ -81,9 +81,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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">
|
<script type="text/javascript">
|
||||||
$( function()
|
$( function()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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/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/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/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-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/vendor/jquery/jquery_ui/jquery-ui.min.js"></script>
|
||||||
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
|
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
|
||||||
|
|||||||
@@ -94,8 +94,13 @@ class BasketCalculator
|
|||||||
if ( isset( $val['parent_id'] ) and (int)$val['parent_id'] and isset( $val['product-id'] ) )
|
if ( isset( $val['parent_id'] ) and (int)$val['parent_id'] and isset( $val['product-id'] ) )
|
||||||
$permutation = $productRepo->getProductPermutationHash( (int)$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'] ) )
|
if ( !$permutation and isset( $val['attributes'] ) and is_array( $val['attributes'] ) and count( $val['attributes'] ) ) {
|
||||||
$permutation = implode( '|', $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(
|
$quantity_options = $productRepo->getProductPermutationQuantityOptions(
|
||||||
$val['parent_id'] ? $val['parent_id'] : $val['product-id'],
|
$val['parent_id'] ? $val['parent_id'] : $val['product-id'],
|
||||||
|
|||||||
@@ -323,7 +323,9 @@ class TransportRepository
|
|||||||
$transports[] = $tr;
|
$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++ ) {
|
for ( $i = 0; $i < count( $transports ); $i++ ) {
|
||||||
if ( $transports[$i]['delivery_free'] == 1 ) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -416,7 +416,12 @@ class UpdateRepository
|
|||||||
return $log;
|
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';
|
$log[] = '[OK] Pobrano ' . count( $queries ) . ' zapytań SQL';
|
||||||
$success = 0;
|
$success = 0;
|
||||||
$errors = 0;
|
$errors = 0;
|
||||||
|
|||||||
@@ -60,4 +60,9 @@ class Tpl
|
|||||||
{
|
{
|
||||||
return $this->vars[$name];
|
return $this->vars[$name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
return isset($this->vars[$name]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ class ShopBasketController
|
|||||||
$attributes[] = $val;
|
$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 )
|
foreach( $values as $key => $val )
|
||||||
{
|
{
|
||||||
if ( strpos( $key, 'custom_field' ) !== false )
|
if ( strpos( $key, 'custom_field' ) !== false )
|
||||||
@@ -372,7 +377,9 @@ class ShopBasketController
|
|||||||
'transport_id' => \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ),
|
'transport_id' => \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ),
|
||||||
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
|
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
|
||||||
'transports_methods' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->transportMethodsFront( $basket, $coupon ),
|
'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,
|
'payment_method_id' => $payment_method_id,
|
||||||
'basket_details' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
|
'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 )
|
private function jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id )
|
||||||
{
|
{
|
||||||
|
global $settings;
|
||||||
|
|
||||||
echo json_encode( [
|
echo json_encode( [
|
||||||
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
|
'basket' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-details', [
|
||||||
'basket' => $basket,
|
'basket' => $basket,
|
||||||
@@ -398,7 +407,9 @@ class ShopBasketController
|
|||||||
'products_count' => count( $basket ),
|
'products_count' => count( $basket ),
|
||||||
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
|
'transport_methods' => \Shared\Tpl\Tpl::view( 'shop-basket/basket-transport-methods', [
|
||||||
'transports_methods' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->transportMethodsFront( $basket, $coupon ),
|
'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;
|
exit;
|
||||||
|
|||||||
@@ -80,16 +80,17 @@ class ShopProductController
|
|||||||
{
|
{
|
||||||
global $lang_id;
|
global $lang_id;
|
||||||
|
|
||||||
$combination = '';
|
|
||||||
$selected_values = \Shared\Helpers\Helpers::get( 'selected_values' );
|
$selected_values = \Shared\Helpers\Helpers::get( 'selected_values' );
|
||||||
|
|
||||||
foreach ( $selected_values as $value )
|
// Sort by attribute ID to match permutation_hash order (generated with ksort)
|
||||||
{
|
if ( is_array( $selected_values ) ) {
|
||||||
$combination .= $value;
|
usort( $selected_values, function ( $a, $b ) {
|
||||||
if ( $value != end( $selected_values ) )
|
return (int) explode( '-', $a )[0] - (int) explode( '-', $b )[0];
|
||||||
$combination .= '|';
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$combination = is_array( $selected_values ) ? implode( '|', $selected_values ) : '';
|
||||||
|
|
||||||
$product_id = \Shared\Helpers\Helpers::get( 'product_id' );
|
$product_id = \Shared\Helpers\Helpers::get( 'product_id' );
|
||||||
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
$productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] );
|
||||||
$product = $productRepo->findCached( $product_id, $lang_id );
|
$product = $productRepo->findCached( $product_id, $lang_id );
|
||||||
@@ -102,6 +103,10 @@ class ShopProductController
|
|||||||
private static function getPermutation( $attributes )
|
private static function getPermutation( $attributes )
|
||||||
{
|
{
|
||||||
if ( !is_array( $attributes ) || !count( $attributes ) ) return null;
|
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 );
|
return implode( '|', $attributes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ param(
|
|||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||||
|
|
||||||
# --- Helpers ---
|
# --- Helpers ---
|
||||||
|
|
||||||
@@ -214,7 +215,7 @@ $sqlQueries = @()
|
|||||||
$migrationFile = "migrations/$versionNumber.sql"
|
$migrationFile = "migrations/$versionNumber.sql"
|
||||||
|
|
||||||
if (Test-Path $migrationFile) {
|
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)"
|
Write-Step "Znaleziono migracje SQL: $migrationFile ($($sqlQueries.Count) zapytan)"
|
||||||
} else {
|
} else {
|
||||||
Write-Step "Brak migracji SQL ($migrationFile nie istnieje)"
|
Write-Step "Brak migracji SQL ($migrationFile nie istnieje)"
|
||||||
@@ -322,7 +323,7 @@ $manifest = @{
|
|||||||
|
|
||||||
$manifestJson = $manifest | ConvertTo-Json -Depth 4
|
$manifestJson = $manifest | ConvertTo-Json -Depth 4
|
||||||
$manifestPath = "$updatesDir/ver_${versionNumber}_manifest.json"
|
$manifestPath = "$updatesDir/ver_${versionNumber}_manifest.json"
|
||||||
$manifestJson | Out-File $manifestPath -Encoding UTF8
|
[System.IO.File]::WriteAllText($manifestPath, $manifestJson, $Utf8NoBom)
|
||||||
|
|
||||||
Write-Ok "Utworzono manifest: $manifestPath"
|
Write-Ok "Utworzono manifest: $manifestPath"
|
||||||
|
|
||||||
@@ -330,7 +331,7 @@ Write-Ok "Utworzono manifest: $manifestPath"
|
|||||||
|
|
||||||
if ($sqlQueries.Count -gt 0) {
|
if ($sqlQueries.Count -gt 0) {
|
||||||
$sqlPath = "$updatesDir/ver_${versionNumber}_sql.txt"
|
$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"
|
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" }
|
foreach ($d in $deletedDirs) { $filesContent += "D: ../$d" }
|
||||||
|
|
||||||
$filesPath = "$updatesDir/ver_${versionNumber}_files.txt"
|
$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"
|
Write-Ok "Utworzono legacy files: $filesPath"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +351,7 @@ $versionsFile = "updates/versions.php"
|
|||||||
if (Test-Path $versionsFile) {
|
if (Test-Path $versionsFile) {
|
||||||
$content = Get-Content $versionsFile -Raw
|
$content = Get-Content $versionsFile -Raw
|
||||||
$content = $content -replace '\$current_ver\s*=\s*\d+;', "`$current_ver = $versionInt;"
|
$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"
|
Write-Ok "Zaktualizowano versions.php: `$current_ver = $versionInt"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +364,7 @@ if (Test-Path $changelogFile) {
|
|||||||
|
|
||||||
$changelogContent = Get-Content $changelogFile -Raw
|
$changelogContent = Get-Content $changelogFile -Raw
|
||||||
$changelogContent = $newEntry + $changelogContent
|
$changelogContent = $newEntry + $changelogContent
|
||||||
$changelogContent | Out-File $changelogFile -Encoding UTF8 -NoNewline
|
[System.IO.File]::WriteAllText($changelogFile, $changelogContent, $Utf8NoBom)
|
||||||
Write-Ok "Zaktualizowano changelog.php"
|
Write-Ok "Zaktualizowano changelog.php"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,24 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 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
|
## 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**: Kolumny `min_order_amount` i `max_order_amount` w `pp_shop_payment_methods` — konfigurowalne limity kwotowe per metoda platnosci
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ composer test # standard
|
|||||||
## Aktualny stan
|
## Aktualny stan
|
||||||
|
|
||||||
```text
|
```text
|
||||||
OK (734 tests, 2080 assertions)
|
OK (739 tests, 2089 assertions)
|
||||||
```
|
```
|
||||||
|
|
||||||
Zweryfikowano: 2026-02-22 (ver. 0.304)
|
Zweryfikowano: 2026-02-22 (ver. 0.304)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2349,6 +2349,60 @@ ul.pager {
|
|||||||
white-space: nowrap;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,32 @@ if ( is_array( $this -> transports_methods ) )
|
|||||||
</div>
|
</div>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
<? endforeach; 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">🚚</div>
|
||||||
|
<div class="free-delivery-bar__content">
|
||||||
|
<?php if ($percentage >= 100): ?>
|
||||||
|
<div class="free-delivery-bar__text">Gratulacje! Masz darmową dostawę!</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="free-delivery-bar__text">
|
||||||
|
Zrób zakupy za <?= \Shared\Helpers\Helpers::decimal( $this->free_delivery, 2 );?> zł i otrzymaj darmową dostawę
|
||||||
|
</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ł</strong> do darmowej dostawy.
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<script class="footer" type="text/javascript">
|
<script class="footer" type="text/javascript">
|
||||||
$( function() {
|
$( function() {
|
||||||
|
|
||||||
|
|||||||
@@ -420,4 +420,197 @@ class TransportRepositoryTest extends TestCase
|
|||||||
|
|
||||||
$this->assertEquals([], $repository->forPaymentMethod(0));
|
$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.304.zip
Normal file
BIN
updates/0.30/ver_0.304.zip
Normal file
Binary file not shown.
23
updates/0.30/ver_0.304_manifest.json
Normal file
23
updates/0.30/ver_0.304_manifest.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": "0.304",
|
||||||
|
"date": "2026-02-22",
|
||||||
|
"checksum_zip": "sha256:98199d033813aadab54cbd6cb495218df5df8f7bc5467ddfb132e7e139aff893",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
"updates/0.30/ver_0.303.zip",
|
||||||
|
"updates/0.30/ver_0.303_manifest.json"
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
"autoload/Domain/PaymentMethod/PaymentMethodRepository.php",
|
||||||
|
"autoload/admin/Controllers/ShopPaymentMethodController.php",
|
||||||
|
"templates/shop-basket/basket-payments-methods.php"
|
||||||
|
],
|
||||||
|
"deleted": []
|
||||||
|
},
|
||||||
|
"directories_deleted": [],
|
||||||
|
"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;"
|
||||||
|
],
|
||||||
|
"changelog": "NEW - konfigurowalne limity kwotowe metod platnosci (min/max kwota zamowienia)"
|
||||||
|
}
|
||||||
BIN
updates/0.30/ver_0.305.zip
Normal file
BIN
updates/0.30/ver_0.305.zip
Normal file
Binary file not shown.
32
updates/0.30/ver_0.305_manifest.json
Normal file
32
updates/0.30/ver_0.305_manifest.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"changelog": "FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku",
|
||||||
|
"version": "0.305",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"deleted": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"checksum_zip": "sha256:533907ff12252be3bf32f264f61bfa0baa7aefde38514a9f791166cc776171c9",
|
||||||
|
"sql": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"date": "2026-02-22",
|
||||||
|
"directories_deleted": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,4 +1,16 @@
|
|||||||
<b>ver. 0.304 - 22.02.2026</b><br />
|
<b>ver. 0.306 - 22.02.2026</b><br />
|
||||||
|
FIX - ukrywanie form dostawy gdy nie ma dostepnych form platnosci (filtrowanie po min/max kwoty zamowienia)
|
||||||
|
<hr>
|
||||||
|
<b>ver. 0.305 - 22.02.2026</b><br />
|
||||||
|
FIX - naprawa kolejnosci atrybutow permutacji, NEW - pasek postepu darmowej dostawy w koszyku
|
||||||
|
<hr>
|
||||||
|
<b>ver. 0.304 - 22.02.2026</b><br />
|
||||||
|
NEW - konfigurowalne limity kwotowe metod platnosci (min/max kwota zamowienia)
|
||||||
|
<hr>
|
||||||
|
<b>ver. 0.305 - 22.02.2026</b><br />
|
||||||
|
FIX - naprawa kolejnosci atrybutow permutacji (sortowanie po ID atrybutu), NEW - pasek postepu darmowej dostawy w koszyku, FIX - icheck CSS przeniesiony do globalnego layoutu admina
|
||||||
|
<hr>
|
||||||
|
<b>ver. 0.304 - 22.02.2026</b><br />
|
||||||
NEW - konfigurowalne limity kwotowe metod platnosci (min/max kwota zamowienia), zastapienie hardcoded warunku PayPo
|
NEW - konfigurowalne limity kwotowe metod platnosci (min/max kwota zamowienia), zastapienie hardcoded warunku PayPo
|
||||||
<hr>
|
<hr>
|
||||||
<b>ver. 0.303 - 22.02.2026</b><br />
|
<b>ver. 0.303 - 22.02.2026</b><br />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 304;
|
$current_ver = 306;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user