diff --git a/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php b/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php index d367d995a74..70a0628c979 100644 --- a/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php +++ b/InventoryReservationCli/Command/Input/GetReservationFromCompensationArgument.php @@ -69,7 +69,7 @@ public function __construct( */ private function parseArgument(string $argument): array { - $pattern = '/(?P.*):(?P.*):(?P.*):(?P.*)/'; + $pattern = '/(?P[^:]+):(?P.*):(?P.*):(?P.*)/'; if (preg_match($pattern, $argument, $match)) { return $match; } @@ -92,6 +92,12 @@ public function execute(string $argument): ReservationInterface $this->searchCriteriaBuilder->addFilter('increment_id', $argumentParts['increment_id'], 'eq')->create() ); $order = current($results->getItems()); + + if (!$order) { + throw new InvalidArgumentException( + sprintf('Order with increment_id "%s" not found', $argumentParts['increment_id']) + ); + } return $this->reservationBuilder ->setSku((string)$argumentParts['sku']) diff --git a/InventoryReservationCli/Test/Integration/Model/AppendReservationsTest.php b/InventoryReservationCli/Test/Integration/Model/AppendReservationsTest.php index 6642d86535e..de70b404b74 100644 --- a/InventoryReservationCli/Test/Integration/Model/AppendReservationsTest.php +++ b/InventoryReservationCli/Test/Integration/Model/AppendReservationsTest.php @@ -105,6 +105,120 @@ public function testCompensateMissingReservationsConfigurableProductCustomStock( self::assertCount(0, $inconsistencies); } + /** + * Verify create-compensations command handles SKUs with colons and semicolons correctly. + * This tests the core functionality of PR #3404 and extends it to cover semicolons. + * + * @magentoDataFixture Magento_InventorySalesApi::Test/_files/websites_with_stores.php + * @magentoDataFixture Magento_InventoryApi::Test/_files/sources.php + * @magentoDataFixture Magento_InventoryApi::Test/_files/stocks.php + * @magentoDataFixture Magento_InventoryApi::Test/_files/stock_source_links.php + * @magentoDataFixture Magento_InventorySalesApi::Test/_files/stock_website_sales_channels.php + * @magentoDataFixture Magento_InventoryConfigurableProduct::Test/_files/product_configurable.php + * @magentoDataFixture Magento_InventoryConfigurableProduct::Test/_files/source_items_configurable.php + * @magentoDataFixture Magento_InventoryIndexer::Test/_files/reindex_inventory.php + * @magentoDataFixture Magento_InventoryShipping::Test/_files/create_quote_on_us_website.php + * @magentoDataFixture Magento_InventoryShipping::Test/_files/order_configurable_product.php + * @magentoDbIsolation disabled + * + * @return void + */ + public function testCompensateMissingReservationsWithSpecialCharactersInSku(): void + { + $orderRepository = ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $searchCriteriaBuilder = ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); + + $searchCriteria = $searchCriteriaBuilder + ->addFilter('increment_id', 'created_order_for_test') + ->create(); + + $order = current($orderRepository->getList($searchCriteria)->getItems()); + $this->assertNotFalse($order, 'Order should exist for testing'); + + // Test various argument formats with SKUs containing colons and semicolons + $testCases = [ + 'simple_colon' => [ + 'sku' => 'test:product:with:colons', + 'quantity' => 2.0, + 'stockId' => 1 + ], + 'complex_colon' => [ + 'sku' => 'brand:category:product:variant', + 'quantity' => 1.0, + 'stockId' => 1 + ], + 'leading_colon' => [ + 'sku' => ':product_with_leading_colon', + 'quantity' => 1.5, + 'stockId' => 1 + ], + 'simple_semicolon' => [ + 'sku' => 'test;product;with;semicolons', + 'quantity' => 3.0, + 'stockId' => 1 + ], + 'complex_semicolon' => [ + 'sku' => 'brand;category;product;variant', + 'quantity' => 2.5, + 'stockId' => 1 + ], + 'mixed_special_chars' => [ + 'sku' => 'product:variant;version-1.0_test', + 'quantity' => 1.0, + 'stockId' => 1 + ], + 'leading_semicolon' => [ + 'sku' => ';product_with_leading_semicolon', + 'quantity' => 0.5, + 'stockId' => 1 + ] + ]; + + foreach ($testCases as $testName => $testData) { + $argument = sprintf( + '%s:%s:-%s:%d', + $order->getIncrementId(), + $testData['sku'], + $testData['quantity'], + $testData['stockId'] + ); + + try { + $reservation = $this->getReservationFromCompensationArgument->execute($argument); + + // Verify the reservation was created correctly + $this->assertInstanceOf( + \Magento\InventoryReservationsApi\Model\ReservationInterface::class, + $reservation, + "Failed to create reservation for test case: {$testName}" + ); + + $this->assertEquals( + $testData['sku'], + $reservation->getSku(), + "SKU mismatch for test case: {$testName}" + ); + + $this->assertEquals( + -$testData['quantity'], + $reservation->getQuantity(), + "Quantity mismatch for test case: {$testName}" + ); + + $this->assertEquals( + $testData['stockId'], + $reservation->getStockId(), + "Stock ID mismatch for test case: {$testName}" + ); + + } catch (\Exception $e) { + $errorMessage = "Failed to process argument with special character SKU for test case '{$testName}': "; + $fullMessage = $errorMessage . $e->getMessage() . " | Argument: {$argument}"; + $this->fail($fullMessage); + } + } + } + /** * Load current Inconsistencies *