Developing custom order documents

Last updated on
24 May 2023

Custom order document plugins can be implemented to create order documents with logic specific to your commerce business needs. If you have experience building other Commerce plugins (checkout panes, promotion offers, payment gateways, etc), you should have no difficulty creating an order document plugin. You can use the included Default and Receipt plugins as examples. If you're new to plugins but have experience implementing custom code for your Drupal project, this documentation should help you get started. For the example code below, we assume that we have a custom module: mymodule. And we'll create a new "Basic" custom plugin with unique id: basic.

Basic structure of an order document plugin

Your custom module will need a config folder and a src folder, so create these if they don't already exist. Within the config folder, add a schema folder. Within the src folder, add folders: Plugin > Commerce > OrderDocument.

If your custom module doesn't already have a mymodule.schema.yml file within the config > schema folder, create the file. Add an entry for our Basic plugin configuration, like this:

commerce_order_document.commerce_order_document.plugin.basic:
  type: commerce_order_document_plugin_configuration

For our Basic plugin class, we'll implement the OrderDocumentInterface interface and extend the base class: OrderDocumentBase. You don't necessarily need to extend OrderDocumentBase but doing so makes it easier to get started. OrderDocumentBase provides access to useful services and implementations for most of the methods defined by OrderDocumentInterface.

The one method missing from OrderDocumentBase that must be implemented in our Basic plugin class is a method that returns a render array for the order document:

  public function buildOrderDocument(OrderInterface $order, $entity_print = FALSE);

If you are unfamiliar with Drupal render arrays, you can read about them here.

The annotation or our Basic plugin has three required elements:

  • id - the unique id for our plugin
  • label - a label for our plugin
  • display_label - the end-user facing display label

Let's put all this together in an order document plugin class named Basic.php, located within nested folders Plugin > Commerce > OrderDocument. Here's what that looks like:

<?php

namespace Drupal\mymodule\Plugin\Commerce\OrderDocument;

use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order_document\Plugin\Commerce\OrderDocument\OrderDocumentBase;
use Drupal\commerce_order_document\Plugin\Commerce\OrderDocument\OrderDocumentInterface;

/**
 * Provides the Basic order document.
 *
 * @CommerceOrderDocument(
 *   id = "basic",
 *   label = "Basic",
 *   display_label = "Basic Document",
 * )
 */
class Basic extends OrderDocumentBase implements OrderDocumentInterface {

  /**
   * {@inheritdoc}
   */
  public function buildOrderDocument(OrderInterface $order, $entity_print = FALSE) {
    $body['message'] = [
      '#markup' => $this->t("This is a basic order document."),
    ];
    return $body;
  }

}

Since we used OrderDocumentBase as the basis for our custom order document plugin, we should also update its schema yaml to match the defined configuration options, like this:

commerce_order_document.commerce_order_document.plugin.basic:
  type: commerce_order_document_plugin_configuration
  mapping:
    can_download:
      type: boolean
      label: 'Allow administrative users to download the document'
    can_email:
      type: boolean
      label: 'Allow administrative users to email the document'
    email_subject:
      type: string
      label: 'The document email subject'

After implementing a new plugin, don't forget to rebuild caches!

As you can see, this Basic plugin simply displays a line of static text--it's not much of a document. Also, the "Download Document" and "Email Document" options are both disabled for this document on the Order Documents page. Next we'll look at how to build a theme-able document.

Build a theme-able custom order document

The Receipt order document uses the commerce_order_receipt theme that's part of Commerce Core. See commerce_order_theme(). The Default order document uses a commerce_order_document theme. See: commerce_order_document_theme(). You can use either of these themes for your custom order document. Preprocess hooks can be implemented for additional variables.

Alternatively, you can use hook_theme() to create an entirely new theme for your custom document. Use hook_theme_suggestions_HOOK() to allow template file variations. Also, if your order document will provide PDF downloads, you may want to define an --entity-print version of your template(s). The buildOrderDocument() method includes an $entity_print parameter that can be used to vary the theme used for rendering the document.

While neither the Receipt nor Default order document plugins provide configurable options for building their documents, it is certainly possible to do so. Any information entered by administrative users building order documents can be stored in configuration and then used within the buildOrderDocument() method to generate the documents.

Build configuration options for a custom order document

The OrderDocumentInterface interface for order document plugins extends two Drupal Core interfaces for managing configuration:

  • Drupal\Component\Plugin\ConfigurableInterface
  • Drupal\Core\Plugin\PluginFormInterface

If you implement custom configuration form methods in your order document plugin, that form will appear on the Order Document configuration form whenever your plugin is selected for a new Order Document.

Don't forget to update the schema yaml for your configuration options.

Implementing email methods

The sendDocumentEmail() plugin method includes an optional $option_values array parameter. The value for this parameter can also be sent programmatically when sending emails in response to events or in other custom code. It can also be used in combination with the confirmation form that is displayed when an administrative user clicks the "Email Document" button on the Documents page. In a custom order document plugin, you can implement a method to customize that confirmation form:

  • public function buildOrderConfirmOptions(OrderInterface $order)

This method should return a render array with form elements. When the confirmation form is submitted, the form values are passed into the sendDocumentEmail() method as its $option_values value.

Implementing document download method

The downloadDocument() plugin method uses the lightweight Entity Print module to print the order document to PDF. A custom renderer for order document entities has been implemented as part of the Commerce Order Document module. Its render() method requires two (or more) entities. The first should be an instance of the OrderDocument entity. The second (and additional) should be Order entities. You can see its usage in the OrderDocumentBase::downloadDocument method implementation.

Help improve this page

Page status: No known problems

You can: