diff --git a/admin/templates/components/table-list.php b/admin/templates/components/table-list.php index 4acf9a7..28e01a5 100644 --- a/admin/templates/components/table-list.php +++ b/admin/templates/components/table-list.php @@ -162,7 +162,7 @@ $isCompactColumn = function(array $column): bool {
@@ -312,6 +312,40 @@ $isCompactColumn = function(array $column): bool { + + diff --git a/admin/templates/shop-order/order-details.php b/admin/templates/shop-order/order-details.php index 1561386..a8acd83 100644 --- a/admin/templates/shop-order/order-details.php +++ b/admin/templates/shop-order/order-details.php @@ -184,13 +184,14 @@ $orderId = (int)($this -> order['id'] ?? 0); = $product[ 'message' ] != '' ? 'Wiadomość: ' . $product['message'] : '';?>Wysłałem zamówienie do apilo.com: ID: ' . $order['id'] . ' - ' . $response['id'] . '
'; } } + + // Po wysłaniu zamówień: przetwórz kolejkę sync (płatności/statusy oczekujące na apilo_order_id) + $orderAdminService->processApiloSyncQueue( 10 ); } // sprawdzanie statusów zamówień w apilo.com jeżeli zamówienie nie jest zrealizowane, anulowane lub nieodebrane diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9791af4..2c0739f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,16 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze. --- +## ver. 0.311 (2026-02-23) - Fix race condition Apilo + persistence filtrów + poprawki cen + +- **FIX**: Race condition — callback płatności przed wysłaniem zamówienia do Apilo nie synchronizował płatności (task trafiał w pustkę). Teraz `syncApiloPaymentIfNeeded` i `syncApiloStatusIfNeeded` kolejkują sync do retry gdy `apilo_order_id` jeszcze nie istnieje +- **FIX**: `processApiloSyncQueue` — zamówienia bez `apilo_order_id` były usuwane z kolejki bez synchronizacji. Teraz czekają (max 50 prób ~8h) aż cron wyśle zamówienie do Apilo +- **FIX**: Drugie wywołanie `processApiloSyncQueue` w cronie po wysyłce zamówień — sync płatności/statusów w tym samym cyklu +- **FIX**: Ceny w szczegółach zamówienia (admin + frontend) — gdy `price_brutto_promo` = 0 lub >= ceny regularnej, wyświetla cenę regularną zamiast 0 zł +- **NEW**: Persistence filtrów tabel w panelu admin — localStorage zapamiętuje ostatni widok (filtry, sortowanie, paginacja) i przywraca go przy powrocie do listy. Przycisk "Wyczyść" resetuje zapisany stan + +--- + ## ver. 0.310 (2026-02-23) - Logi integracji w panelu admin - **NEW**: Zakładka "Logi" w sekcji Integracje — podgląd tabeli `pp_log` z paginacją, sortowaniem, filtrami (akcja, wiadomość, ID zamówienia) i rozwijalnym kontekstem JSON diff --git a/templates/shop-order/order-details.php b/templates/shop-order/order-details.php index e81af24..d32eef6 100644 --- a/templates/shop-order/order-details.php +++ b/templates/shop-order/order-details.php @@ -179,7 +179,7 @@ 'id': = (int)$product['product_id'];?>, 'name': '= $product['name'];?>', 'quantity': = $product['quantity'];?>, - 'price': = $product['price_brutto_promo'];?> + 'price': = ((float)$product['price_brutto_promo'] > 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? (float)$product['price_brutto_promo'] : (float)$product['price_brutto'];?> } if ( $product != end( $this -> order['products'] ) ) echo ',';?> endforeach;?> ] diff --git a/tests/Unit/Domain/Order/OrderAdminServiceTest.php b/tests/Unit/Domain/Order/OrderAdminServiceTest.php index 569a0da..b74c78d 100644 --- a/tests/Unit/Domain/Order/OrderAdminServiceTest.php +++ b/tests/Unit/Domain/Order/OrderAdminServiceTest.php @@ -227,4 +227,110 @@ class OrderAdminServiceTest extends TestCase $service = $this->createService(null, null, $settingsRepo); $this->assertSame(150.0, $service->getFreeDeliveryThreshold()); } + + // ========================================================================= + // processApiloSyncQueue — awaiting apilo_order_id + // ========================================================================= + + private function getQueuePath(): string + { + // Musi odpowiadać ścieżce w OrderAdminService::apiloSyncQueuePath() + // dirname(autoload/Domain/Order/, 2) = autoload/ + return dirname(__DIR__, 4) . '/autoload/temp/apilo-sync-queue.json'; + } + + private function writeQueue(array $queue): void + { + $path = $this->getQueuePath(); + $dir = dirname($path); + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } + file_put_contents($path, json_encode($queue, JSON_PRETTY_PRINT)); + } + + private function readQueue(): array + { + $path = $this->getQueuePath(); + if (!file_exists($path)) return []; + $content = file_get_contents($path); + return $content ? json_decode($content, true) : []; + } + + protected function tearDown(): void + { + $path = $this->getQueuePath(); + if (file_exists($path)) { + unlink($path); + } + parent::tearDown(); + } + + public function testProcessApiloSyncQueueKeepsTaskWhenApiloOrderIdIsNull(): void + { + // Zamówienie bez apilo_order_id — task powinien zostać w kolejce + $this->writeQueue([ + '42' => [ + 'order_id' => 42, + 'payment' => 1, + 'status' => null, + 'attempts' => 0, + 'last_error' => 'awaiting_apilo_order', + 'updated_at' => '2026-01-01 00:00:00', + ], + ]); + + $orderRepo = $this->createMock(OrderRepository::class); + $orderRepo->method('findRawById') + ->with(42) + ->willReturn([ + 'id' => 42, + 'apilo_order_id' => null, + 'paid' => 1, + 'summary' => '100.00', + ]); + + $service = new OrderAdminService($orderRepo); + $processed = $service->processApiloSyncQueue(10); + + $this->assertSame(1, $processed); + + $queue = $this->readQueue(); + $this->assertArrayHasKey('42', $queue); + $this->assertSame('awaiting_apilo_order', $queue['42']['last_error']); + $this->assertSame(1, $queue['42']['attempts']); + } + + public function testProcessApiloSyncQueueRemovesTaskAfterMaxAttempts(): void + { + // Task z 49 próbami — limit to 50, więc powinien zostać usunięty + $this->writeQueue([ + '42' => [ + 'order_id' => 42, + 'payment' => 1, + 'status' => null, + 'attempts' => 49, + 'last_error' => 'awaiting_apilo_order', + 'updated_at' => '2026-01-01 00:00:00', + ], + ]); + + $orderRepo = $this->createMock(OrderRepository::class); + $orderRepo->method('findRawById') + ->with(42) + ->willReturn([ + 'id' => 42, + 'apilo_order_id' => null, + 'paid' => 1, + 'summary' => '100.00', + ]); + + $service = new OrderAdminService($orderRepo); + $processed = $service->processApiloSyncQueue(10); + + $this->assertSame(1, $processed); + + $queue = $this->readQueue(); + $this->assertArrayNotHasKey('42', $queue); + } } diff --git a/updates/versions.php b/updates/versions.php index 2c56836..d15c6c1 100644 --- a/updates/versions.php +++ b/updates/versions.php @@ -1,5 +1,5 @@ -$current_ver = 310; +$current_ver = 311; for ($i = 1; $i <= $current_ver; $i++) {