diff --git a/autoload/front/Controllers/ShopBasketController.php b/autoload/front/Controllers/ShopBasketController.php index 78398aa..7f2405b 100644 --- a/autoload/front/Controllers/ShopBasketController.php +++ b/autoload/front/Controllers/ShopBasketController.php @@ -5,7 +5,6 @@ class ShopBasketController { private const ORDER_SUBMIT_TOKEN_SESSION_KEY = 'order-submit-token'; private const ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY = 'order-submit-last-order-id'; - private const ORDER_SUBMIT_TOKEN_TTL = 1800; // 30 minutes public static $title = [ 'mainView' => 'Koszyk' @@ -277,6 +276,19 @@ class ShopBasketController exit; } + $existingOrderId = isset( $_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] ) + ? (int)$_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] + : 0; + if ( $existingOrderId > 0 ) + { + $existingOrderHash = $this->orderRepository->findHashById( $existingOrderId ); + if ( $existingOrderHash ) + { + header( 'Location: /zamowienie/' . $existingOrderHash ); + exit; + } + } + $client = \Shared\Helpers\Helpers::get_session( 'client' ); $orderSubmitToken = $this->createOrderSubmitToken(); @@ -296,26 +308,23 @@ class ShopBasketController public function basketSave() { - $basket = \Shared\Helpers\Helpers::get_session( 'basket' ); + $orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true ); $existingOrderId = isset( $_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] ) ? (int)$_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] : 0; - if ( ( !is_array( $basket ) || empty( $basket ) ) && $existingOrderId > 0 ) - { - $existingOrderHash = $this->orderRepository->findHashById( $existingOrderId ); - if ( $existingOrderHash ) - { - $this->logOrder( 'Double-submit detected, redirecting to existing order id=' . $existingOrderId ); - header( 'Location: /zamowienie/' . $existingOrderHash ); - exit; - } - } - - $orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true ); if ( !$this->isValidOrderSubmitToken( $orderSubmitToken ) ) { - $this->logOrder( 'Token validation failed. formToken=' . ($orderSubmitToken ?: '(empty)') ); + if ( $existingOrderId > 0 ) + { + $existingOrderHash = $this->orderRepository->findHashById( $existingOrderId ); + if ( $existingOrderHash ) + { + header( 'Location: /zamowienie/' . $existingOrderHash ); + exit; + } + } + \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) ); - header( 'Location: /koszyk-podsumowanie' ); + header( 'Location: /koszyk' ); exit; } @@ -358,7 +367,7 @@ class ShopBasketController } catch ( \Exception $e ) { - $this->logOrder( 'createFromBasket exception: ' . $e->getMessage() ); + error_log( '[basketSave] createFromBasket exception: ' . $e->getMessage() ); \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) ); header( 'Location: /koszyk' ); exit; @@ -391,7 +400,6 @@ class ShopBasketController } else { - $this->logOrder( 'createFromBasket returned falsy order_id. client_id=' . ($client['id'] ?? 'guest') . ' email=' . \Shared\Helpers\Helpers::get( 'email', true ) ); \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) ); header( 'Location: /koszyk' ); exit; @@ -438,6 +446,79 @@ class ShopBasketController ] ); } + public function basketUpdateCustomFields() + { + $basket = \Shared\Helpers\Helpers::get_session( 'basket' ); + $product_code = \Shared\Helpers\Helpers::get( 'product_code' ); + + if ( !isset( $basket[ $product_code ] ) ) + { + echo json_encode( [ 'result' => 'error', 'message' => 'Pozycja nie istnieje w koszyku' ] ); + exit; + } + + $position = $basket[ $product_code ]; + $new_custom_fields = []; + $custom_fields_raw = \Shared\Helpers\Helpers::get( 'custom_field' ); + + if ( is_array( $custom_fields_raw ) ) + { + foreach ( $custom_fields_raw as $field_id => $value ) + { + $new_custom_fields[ (int)$field_id ] = $value; + } + } + + $productRepo = new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ); + $missing_fields = []; + + foreach ( $new_custom_fields as $field_id => $value ) + { + $field_meta = $productRepo->findCustomFieldCached( $field_id ); + if ( $field_meta && (int)$field_meta['is_required'] === 1 && trim( $value ) === '' ) + { + $missing_fields[] = $field_meta['name']; + } + } + + if ( count( $missing_fields ) > 0 ) + { + echo json_encode( [ 'result' => 'error', 'message' => 'Wypełnij wymagane pola: ' . implode( ', ', $missing_fields ) ] ); + exit; + } + + $attributes_implode = ''; + if ( isset( $position['attributes'] ) && is_array( $position['attributes'] ) && count( $position['attributes'] ) > 0 ) + { + $attributes_implode = implode( '|', $position['attributes'] ); + } + + $message = isset( $position['message'] ) ? $position['message'] : ''; + $new_product_code = md5( $position['product-id'] . $attributes_implode . $message . json_encode( $new_custom_fields ) ); + + if ( $new_product_code === $product_code ) + { + $basket[ $product_code ]['custom_fields'] = $new_custom_fields; + } + elseif ( isset( $basket[ $new_product_code ] ) ) + { + $basket[ $new_product_code ]['quantity'] += $position['quantity']; + unset( $basket[ $product_code ] ); + } + else + { + $position['custom_fields'] = $new_custom_fields; + $basket[ $new_product_code ] = $position; + unset( $basket[ $product_code ] ); + } + + $basket = ( new \Domain\Promotion\PromotionRepository( $GLOBALS['mdb'] ) )->findPromotion( $basket ); + \Shared\Helpers\Helpers::set_session( 'basket', $basket ); + + echo json_encode( [ 'result' => 'ok' ] ); + exit; + } + private function jsonBasketResponse( $basket, $coupon, $lang_id, $basket_transport_method_id ) { global $settings; @@ -463,23 +544,8 @@ class ShopBasketController private function createOrderSubmitToken() { - $existingTokenData = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : null; - - if ( is_array( $existingTokenData ) && !empty( $existingTokenData['token'] ) && !empty( $existingTokenData['created_at'] ) ) - { - $age = time() - (int)$existingTokenData['created_at']; - if ( $age < self::ORDER_SUBMIT_TOKEN_TTL ) - { - \Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ); - return $existingTokenData['token']; - } - } - $token = $this->generateOrderSubmitToken(); - \Shared\Helpers\Helpers::set_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY, [ - 'token' => $token, - 'created_at' => time() - ] ); + \Shared\Helpers\Helpers::set_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY, $token ); \Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ); return $token; @@ -502,26 +568,10 @@ class ShopBasketController if ( !$token ) return false; - $tokenData = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : null; - - if ( is_string( $tokenData ) ) - { - $sessionToken = $tokenData; - if ( !$sessionToken ) - return false; - - return function_exists( 'hash_equals' ) ? hash_equals( $sessionToken, $token ) : $sessionToken === $token; - } - - if ( !is_array( $tokenData ) || empty( $tokenData['token'] ) || empty( $tokenData['created_at'] ) ) + $sessionToken = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? (string)$_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : ''; + if ( !$sessionToken ) return false; - $age = time() - (int)$tokenData['created_at']; - if ( $age > self::ORDER_SUBMIT_TOKEN_TTL ) - return false; - - $sessionToken = (string)$tokenData['token']; - if ( function_exists( 'hash_equals' ) ) return hash_equals( $sessionToken, $token ); @@ -532,12 +582,4 @@ class ShopBasketController { \Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY ); } - - private function logOrder( $message ) - { - $logDir = $_SERVER['DOCUMENT_ROOT'] . '/logs'; - $logFile = $logDir . '/logs-order-' . date( 'Y-m-d' ) . '.log'; - $line = '[' . date( 'Y-m-d H:i:s' ) . '] ' . $message . PHP_EOL; - @file_put_contents( $logFile, $line, FILE_APPEND | LOCK_EX ); - } } diff --git a/templates/shop-basket/_partials/product-custom-fields.php b/templates/shop-basket/_partials/product-custom-fields.php index d0b651f..8d2c805 100644 --- a/templates/shop-basket/_partials/product-custom-fields.php +++ b/templates/shop-basket/_partials/product-custom-fields.php @@ -1,26 +1,52 @@ custom_fields ) : ?> - custom_fields as $key => $val ) : ?> - findCustomFieldCached( $key ); ?> - +
+ custom_fields as $key => $val ) : ?> + findCustomFieldCached( $key ); ?> + - -
-
- + +
+
+ +
+
+ +
-
- + +
+
+ +
+
+ <?= htmlspecialchars( $custom_field['name'] );?> +
+ + + Edytuj personalizację +
+ + + diff --git a/templates/shop-basket/basket-details.php b/templates/shop-basket/basket-details.php index 8708c92..d82a063 100644 --- a/templates/shop-basket/basket-details.php +++ b/templates/shop-basket/basket-details.php @@ -61,7 +61,8 @@
$position['custom_fields'] + 'custom_fields' => $position['custom_fields'], + 'product_code' => $position_hash ] ); ?>
diff --git a/templates/shop-basket/basket.php b/templates/shop-basket/basket.php index 67f3952..479b2e3 100644 --- a/templates/shop-basket/basket.php +++ b/templates/shop-basket/basket.php @@ -508,4 +508,62 @@ console.warn('#orlen_point_id nie został znaleziony.'); } }); + + // edycja personalizacji produktu w koszyku + $(document).on('click', '.btn-edit-custom-fields', function(e) { + e.preventDefault(); + var $display = $(this).closest('.custom-fields-display'); + var productCode = $display.data('product-code'); + $display.hide(); + $display.siblings('.custom-fields-edit[data-product-code="' + productCode + '"]').show(); + }); + + $(document).on('click', '.btn-cancel-custom-fields', function(e) { + e.preventDefault(); + var $edit = $(this).closest('.custom-fields-edit'); + var productCode = $edit.data('product-code'); + $edit.hide(); + $edit.siblings('.custom-fields-display[data-product-code="' + productCode + '"]').show(); + }); + + $(document).on('click', '.btn-save-custom-fields', function(e) { + e.preventDefault(); + var $edit = $(this).closest('.custom-fields-edit'); + var productCode = $edit.data('product-code'); + + var valid = true; + $edit.find('input[required]').each(function() { + if ($.trim($(this).val()) === '') { + $(this).css('border-color', 'red'); + valid = false; + } else { + $(this).css('border-color', ''); + } + }); + + if (!valid) { + alert('Wypełnij wszystkie wymagane pola'); + return; + } + + var formData = { product_code: productCode }; + $edit.find('input[name^="custom_field"]').each(function() { + formData[$(this).attr('name')] = $(this).val(); + }); + + $.ajax({ + type: 'POST', + cache: false, + url: '/shopBasket/basket_update_custom_fields', + data: formData, + success: function(response) { + var data = jQuery.parseJSON(response); + if (data.result === 'ok') { + location.reload(); + } else { + alert(data.message || 'Wystąpił błąd'); + } + } + }); + }); \ No newline at end of file diff --git a/templates_user/shop-basket/basket-details.php b/templates_user/shop-basket/basket-details.php index 8ae9cff..2e44dec 100644 --- a/templates_user/shop-basket/basket-details.php +++ b/templates_user/shop-basket/basket-details.php @@ -45,7 +45,8 @@
$position['custom_fields'] + 'custom_fields' => $position['custom_fields'], + 'product_code' => $position_hash ] ); ?>
@@ -112,4 +113,4 @@
Brak produktów w koszyku
-
\ No newline at end of file +
diff --git a/templates_user/shop-basket/basket.php b/templates_user/shop-basket/basket.php index e0014cb..4bbd75a 100644 --- a/templates_user/shop-basket/basket.php +++ b/templates_user/shop-basket/basket.php @@ -537,4 +537,62 @@ console.warn('#orlen_point_id nie został znaleziony.'); } }); - \ No newline at end of file + + // edycja personalizacji produktu w koszyku + $(document).on('click', '.btn-edit-custom-fields', function(e) { + e.preventDefault(); + var $display = $(this).closest('.custom-fields-display'); + var productCode = $display.data('product-code'); + $display.hide(); + $display.siblings('.custom-fields-edit[data-product-code="' + productCode + '"]').show(); + }); + + $(document).on('click', '.btn-cancel-custom-fields', function(e) { + e.preventDefault(); + var $edit = $(this).closest('.custom-fields-edit'); + var productCode = $edit.data('product-code'); + $edit.hide(); + $edit.siblings('.custom-fields-display[data-product-code="' + productCode + '"]').show(); + }); + + $(document).on('click', '.btn-save-custom-fields', function(e) { + e.preventDefault(); + var $edit = $(this).closest('.custom-fields-edit'); + var productCode = $edit.data('product-code'); + + var valid = true; + $edit.find('input[required]').each(function() { + if ($.trim($(this).val()) === '') { + $(this).css('border-color', 'red'); + valid = false; + } else { + $(this).css('border-color', ''); + } + }); + + if (!valid) { + alert('Wypełnij wszystkie wymagane pola'); + return; + } + + var formData = { product_code: productCode }; + $edit.find('input[name^="custom_field"]').each(function() { + formData[$(this).attr('name')] = $(this).val(); + }); + + $.ajax({ + type: 'POST', + cache: false, + url: '/shopBasket/basket_update_custom_fields', + data: formData, + success: function(response) { + var data = jQuery.parseJSON(response); + if (data.result === 'ok') { + location.reload(); + } else { + alert(data.message || 'Wystąpił błąd'); + } + } + }); + }); +