Order Management feature integration
Edit on GitHubThis document describes how to integrate the Order Management feature into a Spryker project.
The following feature integration guide expects the basic feature to be in place. It only adds the following functionalities:
- Order cancellation behavior
- Show
display names
for order item states - Invoice generation
- Custom order reference
Install feature core
Follow the steps below to install the Order Management feature core.
Prerequisites
To start feature integration, overview and install the necessary features:
NAME | VERSION |
---|---|
Spryker Core | 202212.0 |
Mailing & Notifications | 202212.0 |
Order Management | 202212.0 |
Persistent Cart | 202212.0 |
1) Install the required modules using Composer
Install the required modules:
composer require spryker-feature/order-management: "202212.0r" --update-with-dependencies
Make sure that the following modules have been installed:
MODULE | EXPECTED DIRECTORY |
---|---|
OrderCustomReference | vendor/spryker/order-custom-reference |
OrderCustomReferenceGui | vendor/spryker/order-custom-reference-gui |
2) Set up database schema and transfer objects
Apply database changes and generate transfer changes:
console transfer:generate
console propel:install
console transfer:entity:generate
console frontend:zed:build
Make sure that the following changes have been applied in the database:
DATABASE ENTITY | TYPE | EVENT |
---|---|---|
spy_sales_order_invoice | table | created |
spy_sales_order.order_custom_reference | column | created |
Make sure that the following changes have been applied in transfer objects:
TRANSFER | TYPE | EVENT | PATH |
---|---|---|---|
OrderInvoice | class | created | src/Generated/Shared/Transfer/OrderInvoiceTransfer |
OrderInvoiceSendRequest | class | created | src/Generated/Shared/Transfer/OrderInvoiceSendRequestTransfer |
OrderInvoiceSendResponse | class | created | src/Generated/Shared/Transfer/OrderInvoiceSendResponseTransfer |
OrderInvoiceCriteria | class | created | src/Generated/Shared/Transfer/OrderInvoiceCriteriaTransfer |
OrderInvoiceCollection | class | created | src/Generated/Shared/Transfer/OrderInvoiceCollectionTransfer |
OrderInvoiceResponse | class | created | src/Generated/Shared/Transfer/OrderInvoiceResponseTransfer |
Mail.recipientBccs | property | created | src/Generated/Shared/Transfer/MailTransfer |
OrderCustomReferenceResponse | class | created | Generated/Shared/Transfer/OrderCustomReferenceResponseTransfer |
Quote.orderCustomReference | property | created | Generated/Shared/Transfer/QuoteTransfer |
QuoteUpdateRequestAttributes.orderCustomReference | property | created | Generated/Shared/Transfer/QuoteUpdateRequestAttributesTransfer |
Order.orderCustomReference | property | created | Generated/Shared/Transfer/OrderTransfer |
3) Set up configuration
Set up the following configuration.
3.1) Configure OMS
- Create the OMS sub-process file:
config/Zed/oms/DummySubprocess/DummyInvoice01.xml
<?xml version="1.0"?>
<statemachine
xmlns="spryker:oms-01"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="spryker:oms-01 http://static.spryker.com/oms-01.xsd">
<process name="DummyInvoice">
<states>
<state name="invoice generated"/>
</states>
<transitions>
<transition>
<source>confirmed</source>
<target>invoice generated</target>
<event>invoice-generate</event>
</transition>
<transition>
<source>invoice generated</source>
<target>waiting</target>
<target>invoice-generated</target>
</transition>
</transitions>
<events>
<event name="invoice-generate" manual="true" command="Invoice/Generate"/>
<event name="invoice-generated" onEnter="true"/>
</events>
</process>
</statemachine>
Verify the invoice state machine configuration in the next step.
- Using the following process as an example, adjust your OMS state-machine configuration according to your project’s requirements.
config/Zed/oms/DummyPayment01.xml
<?xml version="1.0"?>
<statemachine
xmlns="spryker:oms-01"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="spryker:oms-01 http://static.spryker.com/oms-01.xsd">
<process name="DummyPayment01" main="true">
<subprocesses>
<process>DummyInvoice</process>
</subprocesses>
<states>
<state name="new" reserved="true" display="oms.state.new">
<flag>cancellable</flag>
</state>
<state name="payment pending" reserved="true" display="oms.state.payment-pending">
<flag>cancellable</flag>
</state>
<state name="invalid" display="oms.state.invalid">
<flag>exclude from customer</flag>
</state>
<state name="cancelled" display="oms.state.canceled">
<flag>exclude from customer</flag>
</state>
<state name="paid" reserved="true" display="oms.state.paid">
<flag>cancellable</flag>
</state>
<state name="confirmed" reserved="true" display="oms.state.confirmed">
<flag>cancellable</flag>
</state>
<state name="waiting" reserved="true" display="oms.state.waiting"/>
<state name="exported" reserved="true" display="oms.state.exported"/>
<state name="shipped" reserved="true" display="oms.state.shipped"/>
<state name="delivered" display="oms.state.delivered"/>
<state name="closed" display="oms.state.closed"/>
</states>
<transitions>
<transition happy="true" condition="DummyPayment/IsAuthorized">
<source>new</source>
<target>payment pending</target>
<event>authorize</event>
</transition>
<transition>
<source>new</source>
<target>invalid</target>
<event>authorize</event>
</transition>
<transition>
<source>new</source>
<target>cancelled</target>
<event>cancel</event>
</transition>
<transition happy="true" condition="DummyPayment/IsPayed">
<source>payment pending</source>
<target>paid</target>
<event>pay</event>
</transition>
<transition>
<source>payment pending</source>
<target>cancelled</target>
<event>pay</event>
</transition>
<transition>
<source>payment pending</source>
<target>cancelled</target>
<event>cancel</event>
</transition>
<transition happy="true">
<source>paid</source>
<target>confirmed</target>
<event>confirm</event>
</transition>
<transition happy="true">
<source>confirmed</source>
<target>waiting</target>
<event>skip timeout</event>
</transition>
<transition>
<source>confirmed</source>
<target>cancelled</target>
<event>cancel</event>
</transition>
<transition happy="true">
<source>waiting</source>
<target>exported</target>
<event>check giftcard purchase</event>
</transition>
<transition happy="true" condition="GiftCard/IsGiftCard">
<source>waiting</source>
<target>gift card purchased</target>
<event>check giftcard purchase</event>
</transition>
<transition happy="true">
<source>gift card shipped</source>
<target>delivered</target>
<event>complete gift card creation</event>
</transition>
<transition happy="true">
<source>exported</source>
<target>shipped</target>
<event>ship</event>
</transition>
<transition happy="true">
<source>shipped</source>
<target>delivered</target>
<event>stock-update</event>
</transition>
<transition happy="true">
<source>delivered</source>
<target>closed</target>
<event>close</event>
</transition>
</transitions>
<events>
<event name="authorize" onEnter="true"/>
<event name="pay" manual="true" timeout="1 hour" timeoutProcessor="OmsTimeout/Initiation" command="DummyPayment/Pay"/>
<event name="confirm" onEnter="true" manual="true" command="Oms/SendOrderConfirmation"/>
<event name="skip timeout" manual="true" timeout="30 minute"/>
<event name="cancel" manual="true"/>
<event name="export" onEnter="true" manual="true" command="Oms/SendOrderShipped"/>
<event name="ship" manual="true" command="Oms/SendOrderShipped"/>
<event name="stock-update" manual="true"/>
<event name="close" manual="true" timeout="1 hour"/>
</events>
</process>
<process name="DummyInvoice" file="DummySubprocess/DummyInvoice01.xml"/>
</statemachine>
Ensure that you’ve configured OMS:
-
Go to the Back Office > Administration > OMS.
-
Select DummyPayment01 [preview-version] and check the following:
- The
new
,payment pending
,paid
, andconfirmed
states keep thecancellable
tag inside. - The
invoice generated
state has been added.
3.2) Configure the fallback display name prefix
Adjust configuration according to your project’s requirements:
src/Pyz/Zed/Oms/OmsConfig.php
<?php
namespace Pyz\Zed\Oms;
use Spryker\Zed\Oms\OmsConfig as SprykerOmsConfig;
class OmsConfig extends SprykerOmsConfig
{
/**
* Specification:
* - Uses fallback prefix in concatenation with the normalized state name, in case the display property is not defined for the state.
*
* @return string
*/
public function getFallbackDisplayNamePrefix(): string
{
return 'oms.state.';
}
}
3.2) Configure an order invoice template
- Adjust the configuration according to your project’s requirements:
src/Pyz/Zed/SalesInvoice/SalesInvoiceConfig.php
<?php
namespace Pyz\Zed\SalesInvoice;
use Spryker\Zed\SalesInvoice\SalesInvoiceConfig as SprykerSalesInvoiceConfig;
class SalesInvoiceConfig extends SprykerSalesInvoiceConfig
{
/**
* @api
*
* @return string
*/
public function getOrderInvoiceTemplatePath(): string
{
return 'SalesInvoice/Invoice/Invoice.twig';
}
}
- Using the example below, add an order invoice Twig template according to your project’s requirements:
src/Pyz/Zed/SalesInvoice/Presentation/Invoice/Invoice.twig
{# @var order \Generated\Shared\Transfer\OrderTransfer #}
{# @var invoice \Generated\Shared\Transfer\OrderInvoiceTransfer #}
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0 " />
<title></title>
<style type="text/css">
body {
margin: 0;
padding: 0;
font-size: 16px;
box-sizing: border-box;
}
table {
max-width: 600px;
margin: 0 auto;
border-collapse: collapse;
}
.products-table {
border: 1px solid #000;
}
.products-table td,
.products-table th {
padding: 5px 10px;
}
.background-gray {
background: #e6e6e6;
}
.text-small {
font-size: 13px;
}
.spacing-bottom {
padding-bottom: 15px;
}
.spacing-right {
padding-right: 15px;
}
.align-top {
vertical-align: top;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
</style>
</head>
<body>
<table>
<tr>
<td width="300" class="align-top">
<img src="" width="200" alt="Logo">
</td>
<td width="300">
<strong>{{ 'order_invoice.invoice_template.company.name' | trans }}</strong>
<div class="spacing-bottom text-small">{{ 'order_invoice.invoice_template.company.group' | trans }}</div>
<div class="spacing-bottom text-small">{{ 'order_invoice.invoice_template.company.address' | trans | raw }}</div>
</td>
</tr>
<tr>
<td width="300">
<div class="spacing-bottom spacing-right">
<strong>{{ 'order_invoice.invoice_template.merchant.name' | trans }}</strong>
<div class="text-small">{{ 'order_invoice.invoice_template.merchant.address' | trans }}</div>
</div>
<div class="spacing-bottom">
{{ order.billingAddress.firstName }} {{ order.billingAddress.lastName }}<br>
{{ order.billingAddress.address1 }} {{ order.billingAddress.address2 }} {{ order.billingAddress.address3 }}<br>
{{ order.billingAddress.zipcode }} {{ order.billingAddress.city }}<br>
{{ order.billingAddress.region }}
</div>
</td>
<td width="300" class="align-top">
<div class="spacing-bottom">{{ invoice.issueDate | date('d. M Y') }}</div>
</td>
</tr>
</table>
<table>
<tr>
<td width="600">
<div class="spacing-bottom">
<strong>{{ 'order_invoice.invoice_template.reference' | trans }} {{ invoice.reference }}</strong>
</div>
</td>
</tr>
<tr>
<td width="600">
<div class="spacing-bottom">{{ 'order_invoice.invoice_template.introduction' | trans }}</div>
</td>
</tr>
</table>
<table class="products-table">
<thead>
<tr class="background-gray">
<th width="75"><strong>{{ 'order_invoice.invoice_template.table.number' | trans }}</strong></th>
<th width="75"><strong>{{ 'order_invoice.invoice_template.table.quantity' | trans }}</strong></th>
<th width="275" class="text-left"><strong>{{ 'order_invoice.invoice_template.table.name' | trans }}</strong></th>
<th width="100"><strong>{{ 'order_invoice.invoice_template.table.tax' | trans }}</strong></th>
<th width="75"><strong>{{ 'order_invoice.invoice_template.table.price' | trans | raw }}</strong></th>
</tr>
</thead>
<tbody>
{% set linenumber = 0 %}
{% set renderedBundles = [] %}
{% set taxes = {} %}
{% set itemSumByTaxes = {} %}
{% for item in order.items %}
{# @var item \Generated\Shared\Transfer\ItemTransfer #}
{% set taxRate = item.taxRate %}
{% set rateSum = taxes[item.taxRate] | default(0) + item.sumTaxAmountFullAggregation %}
{% set taxes = taxes | merge({ (taxRate): rateSum }) %}
{% set rateItemSum = itemSumByTaxes[taxRate] | default(0) + item.sumPriceToPayAggregation %}
{% set itemSumByTaxes = itemSumByTaxes | merge({ (taxRate): rateItemSum }) %}
{% if item.productBundle is not defined or item.productBundle is null %}
{% set linenumber = linenumber + 1 %}
<tr>
<td class="text-center">{{ linenumber }}</td>
<td class="text-center">{{ item.quantity }}</td>
<td>{{ item.name }}</td>
<td class="text-center">{{ item.taxRate | number_format }}%</td>
<td class="text-center">{{ item.sumPriceToPayAggregation | money(true, order.currencyIsoCode) }}</td>
</tr>
{% endif %}
{% if item.productBundle is defined and item.productBundle is not null %}
{% if item.relatedBundleItemIdentifier not in renderedBundles %}
{# @var productBundle \Generated\Shared\Transfer\ItemTransfer #}
{% set linenumber = linenumber + 1 %}
{% set productBundle = item.productBundle %}
<tr>
<td class="text-center">{{ linenumber }}</td>
<td class="text-center">{{ productBundle.quantity }}</td>
<td>{{ productBundle.name }}</td>
<td class="text-center">{{ productBundle.taxRate | number_format }}%</td>
<td class="text-center">{{ productBundle.sumPriceToPayAggregation | money(true, order.currencyIsoCode) }}</td>
</tr>
{% for bundleditem in order.items %}
{% if item.relatedBundleItemIdentifier == bundleditem.relatedBundleItemIdentifier %}
<tr>
<td></td>
<td class="text-center">{{ bundleditem.quantity }}</td>
<td>{{ bundleditem.name }}</td>
<td class="text-center">{{ bundleditem.taxRate | number_format }}%</td>
<td class="text-center">{{ bundleditem.sumPriceToPayAggregation | money(true, order.currencyIsoCode) }}</td>
</tr>
{% endif %}
{% endfor %}
{% set renderedBundles = renderedBundles | merge([item.relatedBundleItemIdentifier]) %}
{% endif %}
{% endif %}
{% endfor %}
{% for expense in order.expenses %}
{% set linenumber = linenumber + 1 %}
{% set taxRate = expense.taxRate %}
{% set rateSum = taxes[expense.taxRate] | default(0) + expense.sumTaxAmount %}
{% set taxes = taxes | merge({ (taxRate): rateSum }) %}
{% set rateItemSum = itemSumByTaxes[taxRate] | default(0) + expense.sumPriceToPayAggregation %}
{% set itemSumByTaxes = itemSumByTaxes | merge({ (taxRate): rateItemSum }) %}
<tr>
<td class="text-center">{{ linenumber }}</td>
<td></td>
<td>{{ expense.name }}</td>
<td class="text-center">{{ expense.taxRate | number_format }}%</td>
<td class="text-center">{{ expense.sumPrice | money(true, order.currencyIsoCode) }}</td>
</tr>
{% endfor %}
<tr class="background-gray">
<td colspan="3"></td>
<td>{{ 'order_invoice.invoice_template.table.subtotal' | trans }}</td>
<td class="text-center">{{ order.totals.subtotal | money(true, order.currencyIsoCode) }}</td>
</tr>
<tr class="background-gray">
<td colspan="3"></td>
<td>{{ 'order_invoice.invoice_template.table.discount' | trans }}</td>
<td class="text-center">{{ order.totals.discountTotal | money(true, order.currencyIsoCode) }}</td>
</tr>
{% for rate, tax in taxes %}
<tr>
<td colspan="2">{{ 'order_invoice.invoice_template.table.tax.included' | trans({ '%tax_rate%': rate | number_format }) }}</td>
<td class="text-center">{{ (itemSumByTaxes[rate] - tax) | money(true, order.currencyIsoCode) }}</td>
<td class="text-center">{{ 'order_invoice.invoice_template.table.tax.name' | trans }}</td>
<td class="text-center">{{ tax | money(true, order.currencyIsoCode) }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="2">{{ 'order_invoice.invoice_template.table.total.net' | trans }}</td>
<td class="text-center">{{ (order.totals.grandTotal - order.totals.taxTotal.amount) | money(true, order.currencyIsoCode) }}</td>
<td colspan="2"></td>
</tr>
<tr class="background-gray">
<td colspan="3"></td>
<td><strong>{{ 'order_invoice.invoice_template.table.grandtotal' | trans }}</strong></td>
<td class="text-center">{{ order.totals.grandTotal | money(true, order.currencyIsoCode) }}</td>
</tr>
</tbody>
</table>
</body>
</html>
You will be able to verify the invoice template configuration in a later step.
4) Add translations
An oms.state.
prefixed translation key is a combination of the OmsConfig::getFallbackDisplayNamePrefix()
and a normalized state machine name. If you have different OMS state-machine states or a fallback display name prefix, adjust the corresponding translations.
By default, in state machine names, the following applies:
- Spaces are replaced with dashes.
- All the words are decapitalized.
- Append glossary according to your configuration:
src/Pyz/Zed/Checkout/CheckoutDependencyProvider.php
src/data/import/glossary.csv
>sales.error.customer_order_not_found,Customer Order not found.,en_US
sales.error.customer_order_not_found,Die Bestellung wurde nicht gefunden.,de_DE
sales.error.order_cannot_be_canceled_due_to_wrong_item_state,Order cannot be canceled due to wrong item state.,en_US
sales.error.order_cannot_be_canceled_due_to_wrong_item_state,Die Bestellung kann wegen dem falschen Artikelstatus nicht storniert werden.,de_DE
oms.state.new,New,en_US
oms.state.new,Neu,de_DE
oms.state.payment-pending,Payment pending,en_US
oms.state.payment-pending,Ausstehende Zahlung,de_DE
oms.state.invalid,Ivalid,en_US
oms.state.invalid,Ungültig,de_DE
oms.state.canceled,Canceled,en_US
oms.state.canceled,Abgebrochen,de_DE
oms.state.paid,Paid,en_US
oms.state.paid,Bezahlt,de_DE
oms.state.confirmed,Confirmed,en_US
oms.state.confirmed,Bestätigt,de_DE
oms.state.waiting,Waiting,en_US
oms.state.waiting,Warten,de_DE
oms.state.exported,Exported,en_US
oms.state.exported,Exportiert,de_DE
oms.state.shipped,Shipped,en_US
oms.state.shipped,Versandt,de_DE
oms.state.delivered,Delivered,en_US
oms.state.delivered,Geliefert,de_DE
quote_request.status.closed,Closed,en_US
quote_request.status.closed,Geschlossen,de_DE
mail.order_invoice.subject,"Invoice: %invoiceReference%",en_US
mail.order_invoice.subject,"Rechnung: %invoiceReference%",de_DE
order_custom_reference.reference_saved,Custom order reference was successfully saved.,en_US
order_custom_reference.reference_saved,Ihre Bestellreferenz wurde erfolgreich gespeichert.,de_DE
order_custom_reference.reference_not_saved,Custom order reference has not been changed.,en_US
order_custom_reference.reference_not_saved,Ihre Bestellreferenz wurde nicht geändert.,de_DE
order_custom_reference.validation.error.message_invalid_length,Custom order reference length is invalid.,en_US
order_custom_reference.validation.error.message_invalid_length,Die Länge der Bestellreferenz ist ungültig.,de_DE
order_custom_reference.title,Custom Order Reference,en_US
order_custom_reference.title,Ihre Bestellreferenz,de_DE
order_custom_reference.form.placeholder,Add custom order reference,en_US
order_custom_reference.form.placeholder,Ihre Bestellreferenz hinzufügen,de_DE
order_custom_reference.save,Save,en_US
order_custom_reference.save,Speichern,de_DE
- Import data:
console data:import:glossary
Ensure that in the database, the configured data has been added to the spy_glossary
table.
5) Set up behavior
Set up the following behaviors.
5.1) Set up Order Item Display Name
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
CurrencyIsoCodeOrderItemExpanderPlugin | Expands order items with currency codes (ISO). | Spryker\Zed\Sales\Communication\Plugin\Sales | |
StateHistoryOrderItemExpanderPlugin | Expands order items with history states. | Spryker\Zed\Oms\Communication\Plugin\Sales | |
ItemStateOrderItemExpanderPlugin | Expands order items with its item states. | Spryker\Zed\Oms\Communication\Plugin\Sales | |
OrderAggregatedItemStateSearchOrderExpanderPlugin | Expands orders with aggregated item states. | Spryker\Zed\Oms\Communication\Plugin\Sales |
src/Pyz/Zed/Sales/SalesDependencyProvider.php
<?php
namespace Pyz\Zed\Sales;
use Spryker\Zed\Sales\Communication\Plugin\Sales\CurrencyIsoCodeOrderItemExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\Sales\ItemStateOrderItemExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\Sales\OrderAggregatedItemStateSearchOrderExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\Sales\StateHistoryOrderItemExpanderPlugin;
use Spryker\Zed\Sales\SalesDependencyProvider as SprykerSalesDependencyProvider;
class SalesDependencyProvider extends SprykerSalesDependencyProvider
{
/**
* @return \Spryker\Zed\SalesExtension\Dependency\Plugin\OrderItemExpanderPluginInterface[]
*/
protected function getOrderItemExpanderPlugins(): array
{
return [
new CurrencyIsoCodeOrderItemExpanderPlugin(),
new StateHistoryOrderItemExpanderPlugin(),
new ItemStateOrderItemExpanderPlugin(),
];
}
/**
* @return \Spryker\Zed\SalesExtension\Dependency\Plugin\SearchOrderExpanderPluginInterface[]
*/
protected function getSearchOrderExpanderPlugins(): array
{
return [
new OrderAggregatedItemStateSearchOrderExpanderPlugin()
];
}
}
- Make sure that every order item from the
SalesFacade::getOrderItems()
result contains the following:- Currency ISO code
- State history code
- Item state data
- Make sure that every order from the
SalesFacade::getCustomerOrders()
result contains aggregated item state data.
5.2) Set up order cancellation behavior
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
IsCancellableOrderExpanderPlugin | Checks if each order item has the cancellable flag. | Spryker\Zed\Sales\Communication\Plugin\Sales | |
IsCancellableSearchOrderExpanderPlugin | Checks if each order item has the cancellable flag. | Spryker\Zed\Oms\Communication\Plugin\Sales |
src/Pyz/Zed/Sales/SalesDependencyProvider.php
<?php
namespace Pyz\Zed\Sales;
use Spryker\Zed\Sales\SalesDependencyProvider as SprykerSalesDependencyProvider;
use Spryker\Zed\Oms\Communication\Plugin\Sales\IsCancellableOrderExpanderPlugin;
use Spryker\Zed\Oms\Communication\Plugin\Sales\IsCancellableSearchOrderExpanderPlugin;
class SalesDependencyProvider extends SprykerSalesDependencyProvider
{
/**
* @return \Spryker\Zed\SalesExtension\Dependency\Plugin\SearchOrderExpanderPluginInterface[]
*/
protected function getSearchOrderExpanderPlugins(): array
{
return [
new IsCancellableSearchOrderExpanderPlugin(),
];
}
/**
* @return \Spryker\Zed\SalesExtension\Dependency\Plugin\OrderExpanderPluginInterface[]
*/
protected function getOrderHydrationPlugins(): array
{
return [
new IsCancellableOrderExpanderPlugin(),
];
}
}
Ensure that, on the following pages, each order contains the isCancellable
flag:
- The Storefront:
- Order History
- Overview
- The Back Office:
- Overview of Orders
5.3) Set up order invoice generation behavior
Set up the following order invoice generation behaviors.
5.3.1) Set up order invoice mail type
Set up the following plugin:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderInvoiceMailTypePlugin | Email type that prepares an invoice email for an order. | Spryker\Zed\SalesInvoice\Communication\Plugin\Mail |
src/Pyz/Zed/Mail/MailDependencyProvider.php
<?php
namespace Pyz\Zed\Mail;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Mail\Business\Model\Mail\MailTypeCollectionAddInterface;
use Spryker\Zed\Mail\Business\Model\Provider\MailProviderCollectionAddInterface;
use Spryker\Zed\Mail\MailDependencyProvider as SprykerMailDependencyProvider;
use Spryker\Zed\SalesInvoice\Communication\Plugin\Mail\OrderInvoiceMailTypePlugin;
class MailDependencyProvider extends SprykerMailDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Kernel\Container
*/
public function provideBusinessLayerDependencies(Container $container)
{
$container = parent::provideBusinessLayerDependencies($container);
$container->extend(static::MAIL_TYPE_COLLECTION, function (MailTypeCollectionAddInterface $mailCollection) {
$mailCollection
->add(new OrderInvoiceMailTypePlugin());
return $mailCollection;
});
return $container;
}
}
5.3.2) Set up an order invoice OMS command
Set up the following plugin:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
GenerateOrderInvoiceCommandPlugin | A command in the OMS state machine that generates an invoice for an order. | Spryker\Zed\SalesInvoice\Communication\Plugin\Oms |
src/Pyz/Zed/Oms/OmsDependencyProvider.php
<?php
namespace Pyz\Zed\Oms;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Oms\Dependency\Plugin\Command\CommandCollectionInterface;
use Spryker\Zed\Oms\OmsDependencyProvider as SprykerOmsDependencyProvider;
use Spryker\Zed\SalesInvoice\Communication\Plugin\Oms\GenerateOrderInvoiceCommandPlugin;
class OmsDependencyProvider extends SprykerOmsDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Kernel\Container
*/
protected function extendCommandPlugins(Container $container): Container
{
$container->extend(static::COMMAND_PLUGINS, function (CommandCollectionInterface $commandCollection) {
$commandCollection->add(new GenerateOrderInvoiceCommandPlugin(), 'Invoice/Generate');
return $commandCollection;
});
return $container;
}
}
5.3.3) Set up an order invoice OMS command
- Set up the following plugin:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderInvoiceSendConsole | A console command that sends not-yet-sent order invoices via email. | Spryker\Zed\SalesInvoice\Communication\Console |
src/Pyz/Zed/Oms/OmsDependencyProvider.php
<?php
namespace Pyz\Zed\Oms;
use Spryker\Zed\Console\ConsoleDependencyProvider as SprykerConsoleDependencyProvider;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\SalesInvoice\Communication\Console\OrderInvoiceSendConsole;
class ConsoleDependencyProvider extends SprykerConsoleDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Symfony\Component\Console\Command\Command[]
*/
protected function getConsoleCommands(Container $container): array
{
$commands = [
new OrderInvoiceSendConsole(),
];
return $commands;
}
}
- Adjust the scheduler project configuration:
config/Zed/cronjobs/jenkins.php
/* Order invoice */
$jobs[] = [
'name' => 'order-invoice-send',
'command' => '$PHP_BIN vendor/bin/console order:invoice:send',
'schedule' => '*/5 * * * *',
'enable' => true,
'stores' => $allStores,
];
- Apply the scheduler configuration update:
vendor/bin/console scheduler:suspend
vendor/bin/console scheduler:setup
vendor/bin/console scheduler:resume
Make sure that you’ve set up the invoice-related configuration: move at least one item in an order to the invoice generated
state and make sure that, according to your DummyInvoice01.xml
and SalesInvoiceConfig::getOrderInvoiceTemplatePath()
configuration, the correct order invoice template has been assigned to the order (spy_sales_order_invoice
).
Place an order with an invoice and make sure that you receive an invoice within the time configured in the scheduler.
5.4. Set up a custom order reference workflow
Enable the following behaviors by registering the plugins:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderCustomReferenceOrderPostSavePlugin | After an order is saved, persists orderCustomReference in the spy_sales_order schema. |
Spryker\Zed\OrderCustomReference\Communication\Plugin\Sales\ | |
OrderCustomReferenceQuoteFieldsAllowedForSavingProviderPlugin | Returns the QuoteTransfer fields related to a custom order reference. |
Spryker\Zed\OrderCustomReference\Communication\Plugin\Quote |
src/Pyz/Zed/Sales/SalesDependencyProvider.php
<?php
namespace Pyz\Zed\Sales;
use Spryker\Zed\OrderCustomReference\Communication\Plugin\Sales\OrderCustomReferenceOrderPostSavePlugin;
use Spryker\Zed\Sales\SalesDependencyProvider as SprykerSalesDependencyProvider;
class SalesDependencyProvider extends SprykerSalesDependencyProvider
{
/**
* @return \Spryker\Zed\SalesExtension\Dependency\Plugin\OrderPostSavePluginInterface[]
*/
protected function getOrderPostSavePlugins()
{
return [
new OrderCustomReferenceOrderPostSavePlugin(),
];
}
}
src/Pyz/Zed/Quote/QuoteDependencyProvider.php
<?php
namespace Pyz\Zed\Quote;
use Spryker\Zed\OrderCustomReference\Communication\Plugin\Quote\OrderCustomReferenceQuoteFieldsAllowedForSavingProviderPlugin;
use Spryker\Zed\Quote\QuoteDependencyProvider as SprykerQuoteDependencyProvider;
class QuoteDependencyProvider extends SprykerQuoteDependencyProvider
{
/**
* @return \Spryker\Zed\QuoteExtension\Dependency\Plugin\QuoteFieldsAllowedForSavingProviderPluginInterface[]
*/
protected function getQuoteFieldsAllowedForSavingProviderPlugins(): array
{
return [
new OrderCustomReferenceQuoteFieldsAllowedForSavingProviderPlugin(),
];
}
}
Log in and make sure that, at zed.mysprykershop.com/sales/detail
, you can see the Custom Order Reference section with the Edit Reference button in the order details.
5.5. Set up order saving plugins
Set up the following plugins:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderSaverPlugin | Saves an order. | Spryker\Zed\Sales\Communication\Plugin\Checkout | |
OrderTotalsSaverPlugin | Saves order totals. | Spryker\Zed\Sales\Communication\Plugin\Checkout | |
SalesOrderShipmentSaverPlugin | Saves an order shipment. Adds shipment expenses. | Spryker\Zed\Shipment\Communication\Plugin\Checkout | |
OrderItemsSaverPlugin | Saves order items. | Spryker\Zed\Sales\Communication\Plugin\Checkout |
src/Pyz/Zed/Checkout/CheckoutDependencyProvider.php
<?php
namespace Pyz\Zed\Checkout;
use Spryker\Zed\Sales\Communication\Plugin\Checkout\OrderItemsSaverPlugin;
use Spryker\Zed\Sales\Communication\Plugin\Checkout\OrderSaverPlugin;
use Spryker\Zed\Sales\Communication\Plugin\Checkout\OrderTotalsSaverPlugin;
use Spryker\Zed\Shipment\Communication\Plugin\Checkout\SalesOrderShipmentSavePlugin;
use Spryker\Zed\Checkout\CheckoutDependencyProvider as SprykerCheckoutDependencyProvider;
class CheckoutDependencyProvider extends SprykerCheckoutDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Checkout\Dependency\Plugin\CheckoutSaveOrderInterface[]
*/
protected function getCheckoutOrderSavers(Container $container)
{
/** @var \Spryker\Zed\Checkout\Dependency\Plugin\CheckoutSaveOrderInterface[] $plugins */
$plugins = [
new OrderSaverPlugin(),
new OrderTotalsSaverPlugin(),
new SalesOrderShipmentSavePlugin(),
new OrderItemsSaverPlugin(),
];
return $plugins;
}
}
Make sure that, on the following Storefront pages, even if the display
property is not set in the process definition, the item states are displayed correctly:
- Customer overview
- Order history
- Order details
- Returns
- Return details
Install feature frontend
Follow the steps below to install the Order Management feature front end.
Prerequisites
To start the feature integration, overview and install the necessary features.
NAME | VERSION |
---|---|
Spryker Core | 202212.0 |
Cart | 202212.0 |
Checkout | 202212.0 |
Customer Account Management | 202212.0 |
1) Install the required modules using Composer
Install the required modules:
composer require spryker-feature/order-management: "202212.0" --update-with-dependencies
Make sure that the following modules have been installed:
MODULE | EXPECTED DIRECTORY |
---|---|
OrderCustomReferenceWidget | vendor/spryker-shop/order-custom-reference-widget |
2) Add translations
- Append the glossary according to your configuration:
order_cancel_widget.cancel_order,Cancel Order,en_US
order_cancel_widget.cancel_order,Bestellung stornieren,de_DE
order_cancel_widget.order.cancelled,Order was canceled successfully.,en_US
order_cancel_widget.order.cancelled,Die Bestellung wurde erfolgreich storniert.,de_DE
- Import data:
console data:import:glossary
Ensure that in the database, the configured data has been added to the spy_glossary
table.
3) Enable controllers
Register the following route provider on the Storefront:
PROVIDER | NAMESPACE |
---|---|
OrderCancelWidgetRouteProviderPlugin | SprykerShop\Yves\OrderCancelWidget\Plugin\Router |
src/Pyz/Yves/Router/RouterDependencyProvider.php
<?php
namespace Pyz\Yves\Router;
use Spryker\Yves\Router\RouterDependencyProvider as SprykerRouterDependencyProvider;
use SprykerShop\Yves\OrderCancelWidget\Plugin\Router\OrderCancelWidgetRouteProviderPlugin;
class RouterDependencyProvider extends SprykerRouterDependencyProvider
{
/**
* @return \Spryker\Yves\RouterExtension\Dependency\Plugin\RouteProviderPluginInterface[]
*/
protected function getRouteProvider(): array
{
return [
new OrderCancelWidgetRouteProviderPlugin(),
];
}
}
Ensure that the yves.mysprykershop.com/order/cancel
route is available for POST requests.
4) Set up behavior
Set up the following behaviors.
4.1) Set up an order cancellation behavior
Set up the following plugin:
PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderCancelButtonWidget | Shows a Cancel button on the Storefront. | SprykerShop\Yves\OrderCancelWidget\Widget |
src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php
<?php
namespace Pyz\Yves\ShopApplication;
use SprykerShop\Yves\OrderCancelWidget\Widget\OrderCancelButtonWidget;
use SprykerShop\Yves\ShopApplication\ShopApplicationDependencyProvider as SprykerShopApplicationDependencyProvider;
class ShopApplicationDependencyProvider extends SprykerShopApplicationDependencyProvider
{
/**
* @return string[]
*/
protected function getGlobalWidgets(): array
{
return [
OrderCancelButtonWidget::class,
];
}
}
Ensure the following:
- The
OrderCancelButtonWidget
widget has been registered. - On the Order Details page on the Storefront, the Cancel button is displayed.
- In the item state table column on the Customer Overview and Order History pages on the Storefront, you can see the aggregated order item states.
- On the Return Page on the Storefront, aggregated return item states are displayed .
- On the Order Detail and Return Detail pages on the Storefront, item states are displayed .
5) Enable a route provider plugin
Register the route provider in the Yves application:
PROVIDER | NAMESPACE |
---|---|
OrderCustomReferenceWidgetRouteProviderPlugin | SprykerShop\Yves\OrderCustomReferenceWidget\Plugin\Router |
src/Pyz/Yves/Router/RouterDependencyProvider.php
<?php
namespace Pyz\Yves\Router;
use Spryker\Yves\Router\RouterDependencyProvider as SprykerRouterDependencyProvider;
use SprykerShop\Yves\OrderCustomReferenceWidget\Plugin\Router\OrderCustomReferenceWidgetRouteProviderPlugin;
class RouterDependencyProvider extends SprykerRouterDependencyProvider
{
/**
* @return \Spryker\Yves\RouterExtension\Dependency\Plugin\RouteProviderPluginInterface[]
*/
protected function getRouteProvider(): array
{
return [
new OrderCustomReferenceWidgetRouteProviderPlugin(),
];
}
}
5) Set up widgets
- Register the following plugin to enable widgets:
PLUGIN | DESCRIPTION | PREREQUISITES | NAMESPACE |
---|---|---|---|
OrderCustomReferenceWidget | Edits and shows a custom order reference on the Storefront. | SprykerShop\Yves\OrderCustomReferenceWidget\Widget |
src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php
<?php
namespace Pyz\Yves\ShopApplication;
use SprykerShop\Yves\OrderCustomReferenceWidget\Widget\OrderCustomReferenceWidget;
use SprykerShop\Yves\ShopApplication\ShopApplicationDependencyProvider as SprykerShopApplicationDependencyProvider;
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
class ShopApplicationDependencyProvider extends SprykerShopApplicationDependencyProvider
{
/**
* @return string[]
*/
protected function getGlobalWidgets(): array
{
return [
OrderCustomReferenceWidget::class,
];
}
}
- Run the following command to enable Javascript and CSS changes:
console frontend:yves:build
To make sure that you’ve registered the widget, log in as a customer on the Storefront and check that the Custom order reference form is present on the order view page.
Related features
Integrate the following related features:
FEATURE | REQUIRED FOR THE CURRENT FEATURE | INTEGRATION GUIDE |
---|---|---|
Comments + Order Management feature integration | Comments + Order Management feature integration | |
Glue API: Order Management feature integration | Glue API: Order Management feature integration | |
Company Account + Order Management feature integration | Company Account + Order Management feature integration | |
Product + Order Management feature integration | Product + Order Management feature integration | |
Customer Account Management + Order Management feature integration | Customer Account Management + Order Management feature integration | |
Packaging Units feature integration | Packaging Units feature integration | |
Product + Order Management feature integration | Product + Order Management feature integration | |
Product Options + Order Management feature integration | Product Options + Order Management feature integration |
Thank you!
For submitting the form