diff --git a/app/migrations/Version20170913125128.php b/app/migrations/Version20170913125128.php new file mode 100644 index 00000000000..8ddd520d375 --- /dev/null +++ b/app/migrations/Version20170913125128.php @@ -0,0 +1,55 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE sylius_order_item ADD product_name VARCHAR(255), ADD variant_name VARCHAR(255)'); + + $this->addSql(' + UPDATE sylius_order_item + INNER JOIN sylius_order ON sylius_order_item.order_id = sylius_order.id + INNER JOIN sylius_product_variant ON sylius_order_item.variant_id = sylius_product_variant.id + INNER JOIN sylius_product ON sylius_product_variant.product_id = sylius_product.id + INNER JOIN sylius_product_translation ON sylius_product_translation.translatable_id = sylius_product.id + SET sylius_order_item.product_name = sylius_product_translation.name + WHERE sylius_product_translation.locale = sylius_order.locale_code + + '); + $this->addSql(' + UPDATE sylius_order_item + INNER JOIN sylius_order ON sylius_order_item.order_id = sylius_order.id + INNER JOIN sylius_product_variant ON sylius_order_item.variant_id = sylius_product_variant.id + INNER JOIN sylius_product_variant_translation ON sylius_product_variant_translation.translatable_id = sylius_product_variant.id + SET sylius_order_item.variant_name = sylius_product_variant_translation.name + WHERE sylius_product_variant_translation.locale = sylius_order.locale_code + '); + + $this->addSql('ALTER TABLE sylius_order_item CHANGE product_name product_name VARCHAR(255) DEFAULT NULL, CHANGE variant_name variant_name VARCHAR(255) DEFAULT NULL'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('ALTER TABLE sylius_order_item DROP product_name, DROP variant_name'); + } +} diff --git a/features/account/customer_account/viewing_orders_history/viewing_order_items_with_proper_names.feature b/features/account/customer_account/viewing_orders_history/viewing_order_items_with_proper_names.feature new file mode 100644 index 00000000000..4674e441688 --- /dev/null +++ b/features/account/customer_account/viewing_orders_history/viewing_order_items_with_proper_names.feature @@ -0,0 +1,26 @@ +@customer_account +Feature: Viewing order items with proper names + In order to check some details of my placed order + As an Customer + I want to be able to view items with proper names of my placed order + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Angel T-Shirt" priced at "$39.00" + And the store has a product "Angel Mug" priced at "$19.00" + And the store ships everywhere for free + And the store allows paying with "Cash on Delivery" + And I am a logged in customer + And I placed an order "#00000666" + And I bought an "Angel T-Shirt" and an "Angel Mug" + And I addressed it to "Lucifer Morningstar", "Seaside Fwy", "90802" "Los Angeles" in the "United States" with identical billing address + And I chose "Free" shipping method with "Cash on Delivery" payment + + @ui + Scenario: Viewing basic information about an order + Given the product "Angel T-Shirt" was renamed to "Devil Cardigan" + And the product "Angel Mug" was renamed to "Devil Glass" + When I view the summary of the order "#00000666" + And I should see 2 items in the list + And the product named "Angel T-Shirt" should be in the items list + And the product named "Angel Mug" should be in the items list diff --git a/features/order/managing_orders/seeing_order_items_with_proper_names.feature b/features/order/managing_orders/seeing_order_items_with_proper_names.feature new file mode 100644 index 00000000000..87ca755e8fc --- /dev/null +++ b/features/order/managing_orders/seeing_order_items_with_proper_names.feature @@ -0,0 +1,27 @@ +@managing_orders +Feature: Seeing order items with proper names + In order to see ordered products with proper names + As an Administrator + I want to be able to list items + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Angel T-Shirt" priced at "$39.00" + And the store has a product "Angel Mug" priced at "$19.00" + And the store ships everywhere for free + And the store allows paying with "Cash on Delivery" + And there is a customer "lucy@teamlucifer.com" that placed an order "#00000666" + And the customer bought an "Angel T-Shirt" and an "Angel Mug" + And the customer chose "Free" shipping method to "United States" with "Cash on Delivery" payment + And I am logged in as an administrator + + @ui + Scenario: Seeing order items with proper names + Given the product "Angel T-Shirt" was renamed to "Devil Cardigan" + And the product "Angel Mug" was renamed to "Devil Glass" + When I view the summary of the order "#00000666" + Then it should have 2 items + And the product named "Angel T-Shirt" should be in the items list + And the product named "Angel Mug" should be in the items list + And the order's items total should be "$58.00" + And the order's total should be "$58.00" diff --git a/src/Sylius/Behat/Context/Setup/ProductContext.php b/src/Sylius/Behat/Context/Setup/ProductContext.php index 971dce110bd..b7d9fb378c4 100644 --- a/src/Sylius/Behat/Context/Setup/ProductContext.php +++ b/src/Sylius/Behat/Context/Setup/ProductContext.php @@ -782,6 +782,16 @@ public function thisProductHasBeenDisabled(ProductInterface $product) $this->objectManager->flush(); } + /** + * @Given the product :product was renamed to :productName + */ + public function theProductWasRenamedTo(ProductInterface $product, string $productName): void + { + $product->setName($productName); + + $this->objectManager->flush(); + } + /** * @param string $price * diff --git a/src/Sylius/Behat/Page/Admin/Order/ShowPage.php b/src/Sylius/Behat/Page/Admin/Order/ShowPage.php index 32497cf166c..a01159ab2ab 100644 --- a/src/Sylius/Behat/Page/Admin/Order/ShowPage.php +++ b/src/Sylius/Behat/Page/Admin/Order/ShowPage.php @@ -145,15 +145,24 @@ public function countItems() /** * {@inheritdoc} */ - public function isProductInTheList($productName) + public function isProductInTheList(string $productName): bool { try { + $table = $this->getElement('table'); $rows = $this->tableAccessor->getRowsWithFields( - $this->getElement('table'), + $table, ['item' => $productName] ); - return 1 === count($rows); + foreach ($rows as $row) { + $field = $this->tableAccessor->getFieldFromRow($table, $row, 'item'); + $name = $field->find('css', '.sylius-product-name'); + if (null !== $name && $name->getText() === $productName) { + return true; + } + } + + return false; } catch (\InvalidArgumentException $exception) { return false; } diff --git a/src/Sylius/Behat/Page/Admin/Order/ShowPageInterface.php b/src/Sylius/Behat/Page/Admin/Order/ShowPageInterface.php index ff05f775508..cf567f735f8 100644 --- a/src/Sylius/Behat/Page/Admin/Order/ShowPageInterface.php +++ b/src/Sylius/Behat/Page/Admin/Order/ShowPageInterface.php @@ -103,7 +103,7 @@ public function countItems(); * * @return bool */ - public function isProductInTheList($productName); + public function isProductInTheList(string $productName): bool; /** * @return string diff --git a/src/Sylius/Behat/Page/Shop/Account/Order/ShowPage.php b/src/Sylius/Behat/Page/Shop/Account/Order/ShowPage.php index 3b0be8d0c94..a3e844460e2 100644 --- a/src/Sylius/Behat/Page/Shop/Account/Order/ShowPage.php +++ b/src/Sylius/Behat/Page/Shop/Account/Order/ShowPage.php @@ -122,15 +122,24 @@ public function getPaymentPrice() /** * {@inheritdoc} */ - public function isProductInTheList($name) + public function isProductInTheList(string $productName): bool { try { + $table = $this->getElement('order_items'); $rows = $this->tableAccessor->getRowsWithFields( - $this->getElement('order_items'), - ['item' => $name] + $table, + ['item' => $productName] ); - return 1 === count($rows); + foreach ($rows as $row) { + $field = $this->tableAccessor->getFieldFromRow($table, $row, 'item'); + $name = $field->find('css', '.sylius-product-name'); + if (null !== $name && $name->getText() === $productName) { + return true; + } + } + + return false; } catch (\InvalidArgumentException $exception) { return false; } diff --git a/src/Sylius/Behat/Page/Shop/Account/Order/ShowPageInterface.php b/src/Sylius/Behat/Page/Shop/Account/Order/ShowPageInterface.php index 9fea45da276..21caeec874e 100644 --- a/src/Sylius/Behat/Page/Shop/Account/Order/ShowPageInterface.php +++ b/src/Sylius/Behat/Page/Shop/Account/Order/ShowPageInterface.php @@ -65,11 +65,11 @@ public function countItems(); public function getPaymentPrice(); /** - * @param string $name + * @param string $productName * * @return bool */ - public function isProductInTheList($name); + public function isProductInTheList(string $productName): bool; /** * @return string diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Product/_info.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/Product/_info.html.twig index 8a710b2519a..41c8c0f5f82 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/Product/_info.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/Product/_info.html.twig @@ -1,9 +1,7 @@ -{% set product = variant.product %} -
{% include '@SyliusAdmin/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_admin_product_tiny_thumbnail'} %}
- {{ product.name }} +
{{ item.productName }}
{{ variant.code }} @@ -17,10 +15,10 @@
{% endfor %}
-{% elseif variant.name is not null %} +{% elseif item.variantName is not null %}
- {{ variant.name }} + {{ item.variantName }}
{% endif %} diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/app/state_machine/sylius_order.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/app/state_machine/sylius_order.yml index 1bff13d5d85..8b5e7ebddb3 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/app/state_machine/sylius_order.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/app/state_machine/sylius_order.yml @@ -58,6 +58,10 @@ winzou_state_machine: on: ["create"] do: ["@sylius.customer_order_addresses_saver", "saveAddresses"] args: ["object"] + sylius_set_order_immutable_names: + on: ["create"] + do: ["@sylius.order_item_names_setter", "__invoke"] + args: ["object"] sylius_cancel_payment: on: ["cancel"] do: ["@sm.callback.cascade_transition", "apply"] diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/OrderItem.orm.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/OrderItem.orm.xml index 8b60d7e6bb8..72ab68cdfe8 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/OrderItem.orm.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/doctrine/model/OrderItem.orm.xml @@ -17,6 +17,9 @@ http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> + + + diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml index bfe59b31e2c..8546b173d38 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml @@ -164,5 +164,7 @@ + + diff --git a/src/Sylius/Bundle/ShopBundle/Resources/views/Product/_info.html.twig b/src/Sylius/Bundle/ShopBundle/Resources/views/Product/_info.html.twig index 8cebac94a90..f7024eef4b6 100644 --- a/src/Sylius/Bundle/ShopBundle/Resources/views/Product/_info.html.twig +++ b/src/Sylius/Bundle/ShopBundle/Resources/views/Product/_info.html.twig @@ -3,7 +3,7 @@
{% include '@SyliusShop/Product/_mainImage.html.twig' with {'product': product, 'filter': 'sylius_shop_product_tiny_thumbnail'} %}
- {{ product.name }} +
{{ item.productName }}
{{ variant.code }} @@ -17,10 +17,10 @@
{% endfor %}
-{% elseif variant.name is not null %} +{% elseif item.variantName is not null %}
- {{ variant.name }} + {{ item.variantName }}
{% endif %} diff --git a/src/Sylius/Component/Core/Model/OrderItem.php b/src/Sylius/Component/Core/Model/OrderItem.php index 3e130e30317..15d1b66458b 100644 --- a/src/Sylius/Component/Core/Model/OrderItem.php +++ b/src/Sylius/Component/Core/Model/OrderItem.php @@ -23,9 +23,35 @@ class OrderItem extends BaseOrderItem implements OrderItemInterface */ protected $variant; + /** + * @var string + */ + protected $productName; + + /** + * @var string + */ + protected $variantName; + /** * {@inheritdoc} */ + public function getVariant(): ?ProductVariantInterface + { + return $this->variant; + } + + /** + * {@inheritdoc} + */ + public function setVariant(?ProductVariantInterface $variant): void + { + $this->variant = $variant; + } + + /** + * @return ProductInterface|null + */ public function getProduct(): ?ProductInterface { return $this->variant->getProduct(); @@ -34,17 +60,33 @@ public function getProduct(): ?ProductInterface /** * {@inheritdoc} */ - public function getVariant(): ?ProductVariantInterface + public function getProductName(): ?string { - return $this->variant; + return $this->productName ?: $this->variant->getProduct()->getName(); } /** * {@inheritdoc} */ - public function setVariant(?ProductVariantInterface $variant): void + public function setProductName(?string $productName): void { - $this->variant = $variant; + $this->productName = $productName; + } + + /** + * {@inheritdoc} + */ + public function getVariantName(): ?string + { + return $this->variantName ?: $this->variant->getName(); + } + + /** + * {@inheritdoc} + */ + public function setVariantName(?string $variantName): void + { + $this->variantName = $variantName; } /** diff --git a/src/Sylius/Component/Core/Model/OrderItemInterface.php b/src/Sylius/Component/Core/Model/OrderItemInterface.php index 24442f3a987..66ee73dc1e1 100644 --- a/src/Sylius/Component/Core/Model/OrderItemInterface.php +++ b/src/Sylius/Component/Core/Model/OrderItemInterface.php @@ -32,6 +32,26 @@ public function getVariant(): ?ProductVariantInterface; */ public function setVariant(?ProductVariantInterface $variant): void; + /** + * @return string|null + */ + public function getProductName(): ?string; + + /** + * @param string|null $productName + */ + public function setProductName(?string $productName): void; + + /** + * @return string|null + */ + public function getVariantName(): ?string; + + /** + * @param string|null $variantName + */ + public function setVariantName(?string $variantName): void; + /** * @return int */ diff --git a/src/Sylius/Component/Core/Order/OrderItemNamesSetter.php b/src/Sylius/Component/Core/Order/OrderItemNamesSetter.php new file mode 100644 index 00000000000..7f314359918 --- /dev/null +++ b/src/Sylius/Component/Core/Order/OrderItemNamesSetter.php @@ -0,0 +1,41 @@ +getLocaleCode(); + + /** @var OrderItemInterface $item */ + foreach ($order->getItems() as $item) { + $variant = $item->getVariant(); + + if (null !== $variant) { + $item->setVariantName($variant->getTranslation($localeCode)->getName()); + } + + if (null !== $variant && null !== $variant->getProduct()) { + $item->setProductName($variant->getProduct()->getTranslation($localeCode)->getName()); + } + } + } +} diff --git a/src/Sylius/Component/Core/Order/OrderItemNamesSetterInterface.php b/src/Sylius/Component/Core/Order/OrderItemNamesSetterInterface.php new file mode 100644 index 00000000000..7973898fe3a --- /dev/null +++ b/src/Sylius/Component/Core/Order/OrderItemNamesSetterInterface.php @@ -0,0 +1,24 @@ +getSubtotal()->shouldReturn(19000); } + + function it_has_no_variant_by_default(): void + { + $this->getVariant()->shouldReturn(null); + } } diff --git a/src/Sylius/Component/Core/spec/Order/OrderItemNamesSetterSpec.php b/src/Sylius/Component/Core/spec/Order/OrderItemNamesSetterSpec.php new file mode 100644 index 00000000000..7d18efe76e6 --- /dev/null +++ b/src/Sylius/Component/Core/spec/Order/OrderItemNamesSetterSpec.php @@ -0,0 +1,56 @@ +shouldImplement(OrderItemNamesSetterInterface::class); + } + + function it_sets_product_and_product_variant_names_on_order_items( + OrderInterface $order, + OrderItemInterface $orderItem, + ProductVariantInterface $variant, + ProductVariantTranslationInterface $variantTranslation, + ProductInterface $product, + ProductTranslationInterface $productTranslation + ): void { + $order->getLocaleCode()->willReturn('en_US'); + $order->getItems()->willReturn(new ArrayCollection([$orderItem->getWrappedObject()])); + + $variant->getProduct()->willReturn($product); + $variant->getTranslation('en_US')->willReturn($variantTranslation); + $variantTranslation->getName()->willReturn('Variant name'); + + $product->getTranslation('en_US')->willReturn($productTranslation); + $productTranslation->getName()->willReturn('Product name'); + + $orderItem->setVariantName('Variant name'); + $orderItem->setProductName('Product name'); + + $this->__invoke($order); + } +} diff --git a/tests/DataFixtures/ORM/resources/cart_with_items.yml b/tests/DataFixtures/ORM/resources/cart_with_items.yml index 952558a530d..7713795c37f 100644 --- a/tests/DataFixtures/ORM/resources/cart_with_items.yml +++ b/tests/DataFixtures/ORM/resources/cart_with_items.yml @@ -1,60 +1,3 @@ -Sylius\Component\Core\Model\Order: - cart_with_items: - channel: "@channel_web" - addItem: ["@sw_mug_item", "@hard_available_mug_item"] - currencyCode: "USD" - localeCode: "en_US" - customer: "@customer_oliver" - state: "cart" - -Sylius\Component\Core\Model\OrderItem: - sw_mug_item: - quantity: 2 - addUnit: ["@sw_mug_item_unit1", "@sw_mug_item_unit2"] - variant: "@mug_sw" - order: "@cart_with_items" - hard_available_mug_item: - quantity: 1 - addUnit: ["@hard_available_mug_item_unit"] - variant: "@hard_available_mug" - order: "@cart_with_items" - -Sylius\Component\Core\Model\OrderItemUnit: - sw_mug_item_unit1: - __construct: ["@sw_mug_item"] - sw_mug_item_unit2: - __construct: ["@sw_mug_item"] - hard_available_mug_item_unit: - __construct: ["@hard_available_mug_item"] - -Sylius\Component\Core\Model\Channel: - channel_web: - code: "WEB" - name: "Web Channel" - hostname: "localhost" - description: "Lorem ipsum" - baseCurrency: "@currency" - defaultLocale: "@locale" - color: "black" - enabled: true - taxCalculationStrategy: "order_items_based" - -Sylius\Component\Currency\Model\Currency: - currency: - code: "USD" - -Sylius\Component\Locale\Model\Locale: - locale: - code: "en_US" - -Sylius\Component\Core\Model\Customer: - customer_oliver: - firstName: "Oliver" - lastName: "Queen" - email: "oliver.queen@star-city.com" - emailCanonical: "oliver.queen@star-city.com" - birthday: "<(new \\DateTime())>" - Sylius\Component\Core\Model\Product: mug: code: "MUG" @@ -125,6 +68,63 @@ Sylius\Component\Product\Model\ProductVariantTranslation: name: "Breaking bad mug" translatable: "@hard_available_mug" +Sylius\Component\Core\Model\Order: + cart_with_items: + channel: "@channel_web" + addItem: ["@sw_mug_item", "@hard_available_mug_item"] + currencyCode: "USD" + localeCode: "en_US" + customer: "@customer_oliver" + state: "cart" + +Sylius\Component\Core\Model\OrderItem: + sw_mug_item: + quantity: 2 + addUnit: ["@sw_mug_item_unit1", "@sw_mug_item_unit2"] + variant: "@mug_sw" + order: "@cart_with_items" + hard_available_mug_item: + quantity: 1 + addUnit: ["@hard_available_mug_item_unit"] + variant: "@hard_available_mug" + order: "@cart_with_items" + +Sylius\Component\Core\Model\OrderItemUnit: + sw_mug_item_unit1: + __construct: ["@sw_mug_item"] + sw_mug_item_unit2: + __construct: ["@sw_mug_item"] + hard_available_mug_item_unit: + __construct: ["@hard_available_mug_item"] + +Sylius\Component\Core\Model\Channel: + channel_web: + code: "WEB" + name: "Web Channel" + hostname: "localhost" + description: "Lorem ipsum" + baseCurrency: "@currency" + defaultLocale: "@locale" + color: "black" + enabled: true + taxCalculationStrategy: "order_items_based" + +Sylius\Component\Currency\Model\Currency: + currency: + code: "USD" + +Sylius\Component\Locale\Model\Locale: + locale: + code: "en_US" + +Sylius\Component\Core\Model\Customer: + customer_oliver: + firstName: "Oliver" + lastName: "Queen" + email: "oliver.queen@star-city.com" + emailCanonical: "oliver.queen@star-city.com" + birthday: "<(new \\DateTime())>" + Sylius\Component\Core\Model\ChannelPricing: sw_mug_web_channel_pricing: channelCode: "WEB"