From a1b60c1f251fb9a0f876ec2ef0b778857bfb7a33 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 12 Mar 2026 11:00:23 +0100 Subject: [PATCH] fix: duplikaty zamowien + status COD (is_cod flag) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - summaryView(): guard — redirect do istniejacego zamowienia gdy ORDER_SUBMIT_LAST_ORDER_ID w sesji - basketSave(): try-catch wokol createFromBasket(), wyjatki logowane, koszyk zachowany - OrderRepository: usunieto hardkodowane payment_id == 3, uzywana flaga is_cod - PaymentMethodRepository: nowe pole is_cod w normalizacji, save() i forTransport() SQL - ShopPaymentMethodController: switch "Platnosc przy odbiorze" w formularzu edycji - migrations/0.338.sql: ALTER TABLE pp_shop_payment_methods ADD COLUMN is_cod Co-Authored-By: Claude Sonnet 4.6 --- autoload/Domain/Order/OrderRepository.php | 2 +- .../PaymentMethod/PaymentMethodRepository.php | 5 +++- .../ShopPaymentMethodController.php | 5 ++++ .../Controllers/ShopBasketController.php | 30 +++++++++++++++++-- docs/CHANGELOG.md | 11 +++++++ docs/DATABASE_STRUCTURE.md | 3 ++ migrations/0.338.sql | 3 ++ 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 migrations/0.338.sql diff --git a/autoload/Domain/Order/OrderRepository.php b/autoload/Domain/Order/OrderRepository.php index adfdfa5..b2465a4 100644 --- a/autoload/Domain/Order/OrderRepository.php +++ b/autoload/Domain/Order/OrderRepository.php @@ -814,7 +814,7 @@ class OrderRepository \Shared\Helpers\Helpers::send_email($settings['contact_email'], 'Nowe zamówienie / ' . $settings['firm_name'] . ' / ' . $order['number'] . ' - ' . $order['client_surname'] . ' ' . $order['client_name'], $mail_order); // zmiana statusu w realizacji jeżeli płatność przy odbiorze - if ($payment_id == 3) { + if (!empty($payment_method['is_cod'])) { $this->updateOrderStatus($order_id, 4); $this->insertStatusHistory($order_id, 4, 1); } diff --git a/autoload/Domain/PaymentMethod/PaymentMethodRepository.php b/autoload/Domain/PaymentMethod/PaymentMethodRepository.php index d0fee49..4ddad69 100644 --- a/autoload/Domain/PaymentMethod/PaymentMethodRepository.php +++ b/autoload/Domain/PaymentMethod/PaymentMethodRepository.php @@ -122,6 +122,7 @@ class PaymentMethodRepository '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), + 'is_cod' => (int)(!empty($data['is_cod']) ? 1 : 0), ]; $this->db->update('pp_shop_payment_methods', $row, ['id' => $paymentMethodId]); @@ -240,7 +241,8 @@ class PaymentMethodRepository spm.status, spm.apilo_payment_type_id, spm.min_order_amount, - spm.max_order_amount + spm.max_order_amount, + spm.is_cod FROM pp_shop_payment_methods AS spm INNER JOIN pp_shop_transport_payment_methods AS stpm ON stpm.id_payment_method = spm.id @@ -335,6 +337,7 @@ class PaymentMethodRepository $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); + $row['is_cod'] = (int)($row['is_cod'] ?? 0); return $row; } diff --git a/autoload/admin/Controllers/ShopPaymentMethodController.php b/autoload/admin/Controllers/ShopPaymentMethodController.php index 6ae73dc..ea3be85 100644 --- a/autoload/admin/Controllers/ShopPaymentMethodController.php +++ b/autoload/admin/Controllers/ShopPaymentMethodController.php @@ -184,6 +184,7 @@ class ShopPaymentMethodController 'apilo_payment_type_id' => $paymentMethod['apilo_payment_type_id'] ?? '', 'min_order_amount' => $paymentMethod['min_order_amount'] ?? '', 'max_order_amount' => $paymentMethod['max_order_amount'] ?? '', + 'is_cod' => (int)($paymentMethod['is_cod'] ?? 0), ]; $fields = [ @@ -220,6 +221,10 @@ class ShopPaymentMethodController 'tab' => 'settings', 'options' => $apiloOptions, ]), + FormField::switch('is_cod', [ + 'label' => 'Platnosc przy odbiorze', + 'tab' => 'settings', + ]), FormField::switch('status', [ 'label' => 'Aktywny', 'tab' => 'settings', diff --git a/autoload/front/Controllers/ShopBasketController.php b/autoload/front/Controllers/ShopBasketController.php index e267d48..eb382e4 100644 --- a/autoload/front/Controllers/ShopBasketController.php +++ b/autoload/front/Controllers/ShopBasketController.php @@ -276,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(); @@ -325,7 +338,10 @@ class ShopBasketController exit; } - if ( $order_id = $this->orderRepository->createFromBasket( + $order_id = null; + try + { + $order_id = $this->orderRepository->createFromBasket( $client[ 'id' ], \Shared\Helpers\Helpers::get_session( 'basket' ), \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ), @@ -347,7 +363,17 @@ class ShopBasketController \Shared\Helpers\Helpers::get_session( 'basket_orlen_point_info' ), \Shared\Helpers\Helpers::get_session( 'coupon' ), \Shared\Helpers\Helpers::get_session( 'basket_message' ) - ) ) + ); + } + catch ( \Exception $e ) + { + error_log( '[basketSave] createFromBasket exception: ' . $e->getMessage() ); + \Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) ); + header( 'Location: /koszyk' ); + exit; + } + + if ( $order_id ) { \Shared\Helpers\Helpers::set_session( self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY, (int)$order_id ); \Shared\Helpers\Helpers::alert( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat' ) ); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 144ce54..c63b8f4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,17 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze. --- +## ver. 0.338 (2026-03-12) - Bugfix: duplikaty zamówień + status COD + +- **FIX**: `autoload/front/Controllers/ShopBasketController::summaryView()` — guard przed ponownym złożeniem zamówienia: jeśli sesja zawiera `ORDER_SUBMIT_LAST_ORDER_ID`, użytkownik jest przekierowywany do istniejącego zamówienia zamiast widzieć formularz ponownie +- **FIX**: `autoload/front/Controllers/ShopBasketController::basketSave()` — owinięcie wywołania `createFromBasket()` w try-catch; wyjątek jest logowany przez `error_log()`, użytkownik widzi komunikat błędu, koszyk sesyjny zostaje zachowany +- **FIX**: `autoload/Domain/Order/OrderRepository::createFromBasket()` — usunięcie hardkodowanego `payment_id == 3` do wykrywania płatności przy odbiorze; zamiast tego używana jest flaga `$payment_method['is_cod']` +- **FEATURE**: `autoload/Domain/PaymentMethod/PaymentMethodRepository` — nowa kolumna `is_cod` (normalizacja, zapis w `save()`, kolumna w `forTransport()` SQL) +- **FEATURE**: `autoload/admin/Controllers/ShopPaymentMethodController` — nowe pole "Platnosc przy odbiorze" w formularzu edycji metody płatności +- **MIGRATION**: `migrations/0.338.sql` — `ALTER TABLE pp_shop_payment_methods ADD COLUMN is_cod TINYINT(1) NOT NULL DEFAULT 0` + +--- + ## ver. 0.337 (2026-03-12) - Bezpieczeństwo: ochrona CSRF panelu administracyjnego - **SECURITY**: `autoload/Shared/Security/CsrfToken.php` — nowa klasa z `getToken()`, `validate()`, `regenerate()` (token 64-znakowy hex, `hash_equals()` przeciw timing attacks) diff --git a/docs/DATABASE_STRUCTURE.md b/docs/DATABASE_STRUCTURE.md index efafebd..eb4a94d 100644 --- a/docs/DATABASE_STRUCTURE.md +++ b/docs/DATABASE_STRUCTURE.md @@ -521,10 +521,13 @@ Metody platnosci sklepu (modul `/admin/shop_payment_method`). | 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) | +| is_cod | Platnosc przy odbiorze: 1 = tak, 0 = nie (TINYINT DEFAULT 0) | | 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-03-12 (ver. 0.338):** dodano kolumne `is_cod` — flaga platnosci przy odbiorze, zastepuje hardkodowane `payment_id == 3` w `OrderRepository::createFromBasket()`. + **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. diff --git a/migrations/0.338.sql b/migrations/0.338.sql new file mode 100644 index 0000000..a172582 --- /dev/null +++ b/migrations/0.338.sql @@ -0,0 +1,3 @@ +ALTER TABLE `pp_shop_payment_methods` + ADD COLUMN `is_cod` TINYINT(1) NOT NULL DEFAULT 0 + COMMENT 'Platnosc przy odbiorze (cash on delivery): 1 = tak, 0 = nie';