diff --git a/composer.json b/composer.json index 0eb95a6e..bd4cc422 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "commerceguys/intl": "^1.0.0" }, "require-dev": { + "drupal/entity_print": "^2.2", "drupal/mailsystem": "^4.3" }, "minimum-stability": "dev" diff --git a/modules/order/commerce_order.module b/modules/order/commerce_order.module index d214d8dc..cd1b4028 100644 --- a/modules/order/commerce_order.module +++ b/modules/order/commerce_order.module @@ -45,6 +45,9 @@ function commerce_order_theme($existing, $type, $theme, $path) { 'totals' => NULL, ], ], + 'commerce_order_receipt__entity_print' => [ + 'base hook' => 'commerce_order_receipt' + ], 'commerce_order_total_summary' => [ 'variables' => [ 'order_entity' => NULL, @@ -86,6 +89,20 @@ function template_preprocess_commerce_order_item(array &$variables) { } } +/** + * Implements hook_entity_extra_field_info_alter(). + */ +function commerce_order_entity_extra_field_info_alter(&$info) { + if (isset($info['commerce_order'])) { + // Show the 'View PDF' link by default. + foreach ($info['commerce_order'] as $bundle => &$fields) { + if (isset($fields['display']['entity_print_view_pdf'])) { + $fields['display']['entity_print_view_pdf']['visible'] = TRUE; + } + } + } +} + /** * Implements hook_field_formatter_info_alter(). * diff --git a/modules/order/src/Entity/Order.php b/modules/order/src/Entity/Order.php index 0015a07f..bf9abc03 100644 --- a/modules/order/src/Entity/Order.php +++ b/modules/order/src/Entity/Order.php @@ -55,6 +55,7 @@ use Drupal\profile\Entity\ProfileInterface; * "default" = "Drupal\commerce_order\OrderRouteProvider", * "delete-multiple" = "Drupal\entity\Routing\DeleteMultipleRouteProvider", * }, + * "entity_print" = "Drupal\commerce_order\EntityPrint\OrderRenderer" * }, * base_table = "commerce_order", * admin_permission = "administer commerce_order", diff --git a/modules/order/src/EntityPrint/OrderRenderer.php b/modules/order/src/EntityPrint/OrderRenderer.php new file mode 100644 index 00000000..87ef8605 --- /dev/null +++ b/modules/order/src/EntityPrint/OrderRenderer.php @@ -0,0 +1,103 @@ +orderTotalSummary = $order_total_summary; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $container->get('renderer'), + $container->get('entity_print.asset_renderer'), + $container->get('entity_print.filename_generator'), + $container->get('event_dispatcher'), + $container->get('entity_type.manager'), + $container->get('commerce_order.order_total_summary') + ); + } + + /** + * {@inheritdoc} + */ + public function render(array $orders) { + $profile_view_builder = $this->entityTypeManager->getViewBuilder('profile'); + $build = []; + foreach ($orders as $order) { + assert($order instanceof OrderInterface); + $order_build = [ + '#theme' => 'commerce_order_receipt__entity_print', + '#order_entity' => $order, + '#totals' => $this->orderTotalSummary->buildTotals($order), + ]; + if ($billing_profile = $order->getBillingProfile()) { + $order_build['#billing_information'] = $profile_view_builder->view($billing_profile); + } + $build[] = $order_build; + } + return $build; + } + + /** + * {@inheritdoc} + */ + public function getFilename(array $entities) { + $entities_label = $this->filenameGenerator->generateFilename($entities, static function (OrderInterface $order) { + return $order->id(); + }); + return $this->t('Order @id @receipt', [ + '@id' => $entities_label, + '@receipt' => $this->formatPlural(count($entities), 'receipt', 'receipts'), + ]); + } + +} diff --git a/modules/order/templates/commerce-order-receipt--entity-print.html.twig b/modules/order/templates/commerce-order-receipt--entity-print.html.twig new file mode 100644 index 00000000..b8e25e6a --- /dev/null +++ b/modules/order/templates/commerce-order-receipt--entity-print.html.twig @@ -0,0 +1 @@ +{% extends 'commerce-order-receipt.html.twig' %} diff --git a/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.info.yml b/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.info.yml new file mode 100644 index 00000000..22feacb3 --- /dev/null +++ b/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.info.yml @@ -0,0 +1,8 @@ +name: commerce_order_entity_print_test +type: module +core_version_requirement: ^8.8 || ^9 +package: Testing +dependencies: + - commerce:commerce_order + - commerce:commerce_product + - entity_print:entity_print diff --git a/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.module b/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.module new file mode 100644 index 00000000..9b713698 --- /dev/null +++ b/modules/order/tests/modules/commerce_order_entity_print_test/commerce_order_entity_print_test.module @@ -0,0 +1,17 @@ + [ + 'base hook' => 'commerce_order_receipt' + ], + ]; +} diff --git a/modules/order/tests/modules/commerce_order_entity_print_test/templates/commerce-order-receipt--entity-print.html.twig b/modules/order/tests/modules/commerce_order_entity_print_test/templates/commerce-order-receipt--entity-print.html.twig new file mode 100644 index 00000000..341bacba --- /dev/null +++ b/modules/order/tests/modules/commerce_order_entity_print_test/templates/commerce-order-receipt--entity-print.html.twig @@ -0,0 +1,5 @@ +{% extends 'commerce-order-receipt.html.twig' %} + +{% block additional_information %} + {{ 'Thank you for your order! Printed with entity_print!'|t }} +{% endblock %} diff --git a/modules/order/tests/src/Kernel/EntityPrintOrderRendererTest.php b/modules/order/tests/src/Kernel/EntityPrintOrderRendererTest.php new file mode 100644 index 00000000..b63e64e8 --- /dev/null +++ b/modules/order/tests/src/Kernel/EntityPrintOrderRendererTest.php @@ -0,0 +1,142 @@ +entityTypeManager->getDefinition('commerce_order'); + $this->assertTrue($definition->hasHandlerClass('entity_print')); + $this->assertEquals(OrderRenderer::class, $definition->getHandlerClass('entity_print')); + } + + /** + * Tests the generated filename. + */ + public function testGetFilename() { + $sut = $this->entityTypeManager->getHandler('commerce_order', 'entity_print'); + assert($sut instanceof OrderRenderer); + $order = Order::create([ + 'id' => '123', + 'type' => 'default', + ]); + $filename = $sut->getFilename([$order]); + $this->assertEquals('Order document receipt', (string) $filename); + + $second_order = Order::create([ + 'id' => '789', + 'type' => 'default', + ]); + $filename = $sut->getFilename([$order, $second_order]); + $this->assertEquals('Order document receipts', (string) $filename); + } + + /** + * Tests the rendered build output. + */ + public function testRender() { + $user = $this->createUser(['mail' => $this->randomString() . '@example.com']); + + $product = Product::create([ + 'type' => 'default', + 'title' => 'Default testing product', + ]); + $product->save(); + + $variation1 = ProductVariation::create([ + 'type' => 'default', + 'product_id' => $product->id(), + 'sku' => 'TEST_' . strtolower($this->randomMachineName()), + 'status' => 1, + 'price' => new Price('12.00', 'USD'), + ]); + $variation1->save(); + assert($variation1 instanceof ProductVariation); + + $profile = Profile::create([ + 'type' => 'customer', + 'address' => [ + 'country_code' => 'US', + 'postal_code' => '53177', + 'locality' => 'Milwaukee', + 'address_line1' => 'Pabst Blue Ribbon Dr', + 'administrative_area' => 'WI', + 'given_name' => 'Frederick', + 'family_name' => 'Pabst', + ], + 'uid' => $user->id(), + ]); + $profile->save(); + + /** @var \Drupal\commerce_order\OrderItemStorageInterface $order_item_storage */ + $order_item_storage = $this->container->get('entity_type.manager')->getStorage('commerce_order_item'); + $order_item1 = $order_item_storage->createFromPurchasableEntity($variation1); + $order_item1->save(); + + $payment_gateway = PaymentGateway::create([ + 'id' => 'cod', + 'label' => 'Manual', + 'plugin' => 'manual', + 'configuration' => [ + 'display_label' => 'Cash on delivery', + 'instructions' => [ + 'value' => 'Sample payment instructions.', + 'format' => 'plain_text', + ], + ], + ]); + $payment_gateway->save(); + + $order = Order::create([ + 'type' => 'default', + 'state' => 'draft', + 'mail' => $user->getEmail(), + 'uid' => $user->id(), + 'ip_address' => '127.0.0.1', + 'billing_profile' => $profile, + 'store_id' => $this->store->id(), + 'order_items' => [$order_item1], + 'payment_gateway' => $payment_gateway->id(), + ]); + $order->save(); + + $sut = $this->entityTypeManager->getHandler('commerce_order', 'entity_print'); + assert($sut instanceof OrderRenderer); + + $build = $sut->render([$order]); + $this->render($build); + $this->assertText('Thank you for your order!'); + $this->assertText('Default store'); + $this->assertText('Cash on delivery'); + $this->assertText('Order Total: $12.00'); + $this->assertText('Printed with entity_print!'); + } + +}