Upgrade the Shipment module
Edit on GitHubUpgrading from version 7.* to 8.*
In the version 8.0.0 of the Shipment module, we have added the ability to assign a delivery method to a store in the Back Office. You can find more details about the changes on the Shipment module release page.
Estimated migration time: 5 min
To upgrade to the new version of the module, do the following:
- Upgrade the
Shipmentmodule to the new version:
composer require spryker/shipment:"^8.0.0" --update-with-dependencies
- Prepare database entity schema for each store in the system:
APPLICATION_STORE=DE console propel:schema:copy
APPLICATION_STORE=US console propel:schema:copy
...
- Run the database migration:
console propel:install
console transfer:generate
Upgrading from version 6.* to version 7.*
In this new version of the Shipment module, we have added support of split delivery. You can find more details about the changes on the Shipment module release page.
This release is a part of the Split delivery concept migration. When you upgrade this module version, you should also update all other installed modules in your project to use the same concept as well as to avoid inconsistent behavior. For more information, see Split Delivery Migration Concept.
Estimated migration time: 10 min
To upgrade to the new version of the module, do the following:
- Upgrade the
Shipmentmodule to the new version:
composer require spryker/shipment: "^7.0.0" --update-with-dependencies
- Clean up the database entity schema for each store in the system:
APPLICATION_STORE=DE console propel:schema:copy
APPLICATION_STORE=US console propel:schema:copy
...
- Run the database migration:
console propel:install
console transfer:generate
- Enable the following plugins:
| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|---|---|---|---|
GiftCardShipmentGroupMethodFilterPlugin |
Filters available shipment methods for a shipment group. | None | \Spryker\Zed\GiftCard\Communication\Plugin\Shipment\GiftCardShipmentGroupMethodFilterPlugin |
ShipmentOrderMailExpanderPlugin |
Expands order mail transfer data with shipment groups data. | None | \Spryker\Zed\Shipment\Dependency\Plugin\Oms\ShipmentOrderMailExpanderPlugin |
ShipmentManualEventGrouperPlugin |
Groups manual events by sales shipment ID. | None | \Spryker\Zed\Shipment\Dependency\Plugin\Oms\ShipmentManualEventGrouperPlugin |
src/Pyz/Zed/Shipment/ShipmentDependencyProvider.php
<?php
namespace Pyz\Zed\Shipment;
use Spryker\Zed\GiftCard\Communication\Plugin\Shipment\GiftCardShipmentGroupMethodFilterPlugin;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Shipment\ShipmentDependencyProvider as SprykerShipmentDependencyProvider;
class ShipmentDependencyProvider extends SprykerShipmentDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\ShipmentExtension\Dependency\Plugin\ShipmentMethodFilterPluginInterface[]
*/
protected function getMethodFilterPlugins(Container $container)
{
return [
new GiftCardShipmentGroupMethodFilterPlugin(),
];
}
}
src/Pyz/Zed/Oms/OmsDependencyProvider.php
<?php
/**
* This file is part of the Spryker Suite.
* For full license information, view the LICENSE file that was distributed with this source code.
*/
namespace Pyz\Zed\Oms;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Oms\OmsDependencyProvider as SprykerOmsDependencyProvider;
use Spryker\Zed\Shipment\Dependency\Plugin\Oms\ShipmentManualEventGrouperPlugin;
use Spryker\Zed\Shipment\Dependency\Plugin\Oms\ShipmentOrderMailExpanderPlugin;
class OmsDependencyProvider extends SprykerOmsDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\OmsExtension\Dependency\Plugin\OmsOrderMailExpanderPluginInterface[]
*/
protected function getOmsOrderMailExpanderPlugins(Container $container)
{
return [
new ShipmentOrderMailExpanderPlugin(),
];
}
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\OmsExtension\Dependency\Plugin\OmsManualEventGrouperPluginInterface[]
*/
protected function getOmsManualEventGrouperPlugins(Container $container)
{
return [
new ShipmentManualEventGrouperPlugin(),
];
}
}
- Enable project configuration:
<?php
namespace Pyz\Shared\Shipment;
use Spryker\Shared\Shipment\ShipmentConfig as SprykerShipmentConfig;
class ShipmentConfig extends SprykerShipmentConfig
{
/**
* @return bool
*/
public function isMultiShipmentSelectionEnabled(): bool
{
return true;
}
}
Upgrading from version 5.* to version 6.*
In version 6, multi-currency prices are introduced for shipment methods, allowing to set up different net and gross price per shipment method for each pre-configured currency.
The spy_shipment_method.default_price database column becomes deprecated. Shipment method prices are stored in spy_shipment_method_price database table instead. spy_shipment_method_price database table holds a store + currency specific gross and net price for each shipment method.
Database structure is as follows:

-
Update
spryker/shipmentmodule to at least 6.0.0 version. -
Update the database:
- Install the database changes by running
vendor/bin/console propel:diff. Propel should generate a migration file with the changes. - Apply the database changes:
vendor/bin/console propel:migrate. - Generate and update ORM models:
vendor/bin/console propel:model:build. - You will find some new classes in your project under
\Orm\Zed\Shipment\Persistencenamespace. It’s important that you make sure that they extend the base classes from the Spryker core, e.g.:\Orm\Zed\Shipment\Persistence\SpyShipmentMethodPriceextends\Spryker\Zed\Shipment\Persistence\Propel\AbstractSpyShipmentMethodPrice\Orm\Zed\Shipment\Persistence\SpyShipmentMethodPriceQueryextends\Spryker\Zed\Shipment\Persistence\Propel\AbstractSpyShipmentMethodPriceQuery
- Install the database changes by running
-
Run
vendor/bin/console transfer:generateto update and generate transfer object changes.Transfer object changes
Property
defaultPriceinShipmentMethodtransfer object is replaced by prices, andstoreCurrencyPriceproperties.prices transferobject property contains the shipment method related prices fromspy_shipment_method_pricedatabase table as aMoneyValuetransfer object collection.storeCurrencyPricetransfer object property contains 1 specific price, based on the pre-configuredstore + pricemode and for the requested currency.
ShipmentMethodtransfer object now contains a shipmentMethodKey property, accordingly to the new database structure. -
Replace the usages of
ShipmentMethod.defaultPricetransfer object property in your custom codes, depending on your requirements. -
Migrate your old database structure by creating a
spy_shipment_method_pricerow for each of yourspy_shipment_methodrows. The number of thespy_shipment_method_pricerows per shipment method should match the number of store and currency pair set up in the configuration. Depending on your requirements, you can setgross/netprices as0/null/any integervalue as cents.
Example migration
<?php
/**
* Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
* Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
*/
namespace Spryker\Zed\Shipment\Communication\Console;
use Orm\Zed\Shipment\Persistence\SpyShipmentMethod;
use Orm\Zed\Shipment\Persistence\SpyShipmentMethodPrice;
use Orm\Zed\Shipment\Persistence\SpyShipmentMethodPriceQuery;
use Orm\Zed\Shipment\Persistence\SpyShipmentMethodQuery;
use Spryker\Zed\Kernel\Communication\Console\Console;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* @method \Spryker\Zed\Shipment\Communication\ShipmentCommunicationFactory getFactory()
*/
class MigrateShipmentMethodPricesConsole extends Console
{
const COMMAND_NAME = 'shipment:price:migrate';
const COMMAND_DESCRIPTION = 'Console command to migrate shipment prices to multi currency implementation.';
/**
* @var int[] Keys are currency iso codes, values are currency ids.
*/
protected static $idCurrencyCache = [];
/**
* @return void
*/
protected function configure()
{
$this->setName(static::COMMAND_NAME);
$this->setDescription(static::COMMAND_DESCRIPTION);
parent::configure();
}
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$storeTransferCollection = $this->getFactory()->getStoreFacade()->getAllStores();
$shipmentMethodCollection = SpyShipmentMethodQuery::create()->find();
if (count($shipmentMethodCollection) === 0) {
$output->writeln('There are no shipment methods to migrate.');
return;
}
if (count($storeTransferCollection) === 0) {
$output->writeln('There are no stores set up to migrate.');
return;
}
$question = new ConfirmationQuestion(
sprintf('Migrate %s shipment methods? (y|n)', count($shipmentMethodCollection)),
false
);
if (!$this->getQuestionHelper()->ask($input, $output, $question)) {
$output->writeln('Aborted.');
return;
}
$storeCurrencies = $this->getStoreCurrencies($storeTransferCollection);
$defaultIdStore = $this->getDefaultIdStore();
$defaultIdCurrency = $this->getDefaultIdCurrency();
foreach ($shipmentMethodCollection as $shipmentMethodEntity) {
$this->processShipmentMethod($shipmentMethodEntity, $storeCurrencies, $defaultIdStore, $defaultIdCurrency);
$output->writeln(sprintf('Shipment method %d is migrated.', $shipmentMethodEntity->getIdShipmentMethod()));
}
$output->writeln('done.');
}
/**
* @param \Orm\Zed\Shipment\Persistence\SpyShipmentMethod $shipmentMethodEntity
* @param array $storeCurrencies
* @param int $defaultIdStore
* @param int $defaultIdCurrency
*
* @return void
*/
protected function processShipmentMethod(SpyShipmentMethod $shipmentMethodEntity, array $storeCurrencies, $defaultIdStore, $defaultIdCurrency)
{
foreach ($storeCurrencies as list($idStore, $idCurrency)) {
$shipmentMethodPriceEntity = SpyShipmentMethodPriceQuery::create()
->filterByFkShipmentMethod($shipmentMethodEntity->getIdShipmentMethod())
->filterByFkStore($idStore)
->filterByFkCurrency($idCurrency)
->findOneOrCreate();
$isDefaultStoreCurrency = $idStore === $defaultIdStore && $idCurrency === $defaultIdCurrency;
$this->setNetPrice($shipmentMethodPriceEntity);
$this->setGrossPrice($shipmentMethodPriceEntity, $shipmentMethodEntity, $isDefaultStoreCurrency);
$shipmentMethodPriceEntity->save();
}
}
/**
* @param \Orm\Zed\Shipment\Persistence\SpyShipmentMethodPrice $shipmentMethodPrice
*
* @return void
*/
protected function setNetPrice(SpyShipmentMethodPrice $shipmentMethodPrice)
{
if ($shipmentMethodPrice->getDefaultNetPrice() !== null) {
return;
}
$shipmentMethodPrice->setDefaultNetPrice(0);
}
/**
* @param \Orm\Zed\Shipment\Persistence\SpyShipmentMethodPrice $shipmentMethodPrice
* @param \Orm\Zed\Shipment\Persistence\SpyShipmentMethod $shipmentMethod
* @param bool $isDefaultStoreCurrency
*
* @return void
*/
protected function setGrossPrice(SpyShipmentMethodPrice $shipmentMethodPrice, SpyShipmentMethod $shipmentMethod, $isDefaultStoreCurrency)
{
if ($shipmentMethodPrice->getDefaultGrossPrice() !== null) {
return;
}
$shipmentMethodPrice->setDefaultGrossPrice($isDefaultStoreCurrency ? (int)$shipmentMethod->getDefaultPrice() : 0);
}
/**
* Returns with a list of available store-currency id pairs.
*
* Example:
* Store 1 has currency 5, 6
* Store 2 has currency 10
* Result: [
* [1, 5],
* [1, 6],
* [2, 10]
* ]
*
* @param \Generated\Shared\Transfer\StoreTransfer[] $storeTransferCollection
*
* @return array
*/
protected function getStoreCurrencies(array $storeTransferCollection)
{
$currencies = [];
foreach ($storeTransferCollection as $storeTransfer) {
foreach ($storeTransfer->getAvailableCurrencyIsoCodes() as $isoCode) {
$currencies[] = [$storeTransfer->getIdStore(), $this->getIdCurrencyByIsoCode($isoCode)];
}
}
return $currencies;
}
/**
* @param string $currencyIsoCode
*
* @return int
*/
protected function getIdCurrencyByIsoCode($currencyIsoCode)
{
if (!isset(static::$idCurrencyCache[$currencyIsoCode])) {
static::$idCurrencyCache[$currencyIsoCode] = $this->getFactory()
->getCurrencyFacade()
->fromIsoCode($currencyIsoCode)
->getIdCurrency();
}
return static::$idCurrencyCache[$currencyIsoCode];
}
/**
* @return int
*/
protected function getDefaultIdCurrency()
{
return $this->getIdCurrencyByIsoCode(
$this->getFactory()
->getStoreFacade()
->getCurrentStore()
->getDefaultCurrencyIsoCode()
);
}
/**
* @return int
*/
protected function getDefaultIdStore()
{
return $this->getFactory()->getStoreFacade()->getCurrentStore()->getIdStore();
}
/**
* @return \Symfony\Component\Console\Helper\QuestionHelper
*/
protected function getQuestionHelper()
{
return $this->getHelper('question');
}
}
- Register the prepared multi-currency handling
MoneyCollectFormTypeform type in your project.
Here is the example of MoneyCollectionTypePlugin registration:
<?php
namespace Pyz\Zed\Shipment;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Money\Communication\Plugin\Form\MoneyCollectionFormTypePlugin;
use Spryker\Zed\Shipment\ShipmentDependencyProvider as SprykerShipmentDependencyProvider;
class ShipmentDependencyProvider extends SprykerShipmentDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Kernel\Communication\Form\FormTypeInterface
*/
protected function createMoneyCollectionFormTypePlugin(Container $container)
{
return new MoneyCollectionFormTypePlugin();
}
}
- The
ShipmentFacadeInterface::createMethodmethod now expects “prices”MoneyValuetransfer object collection to be set in the providedShipmentMethodtransfer object. Update your custom calls to this method accordingly. - The
ShipmentFacadeInterface::updateMethodmethod now expects “prices”MoneyValuetransfer object collection to be set in the providedShipmentMethodtransfer object. Update your custom calls to this method accordingly. - The
ShipmentFacadeInterface::getAvailableMethodsmethod applies multi-currency feature:- Does not populate
taxRatetransfer object property anymore in shipment method transfer objects. - Excludes shipment methods which would end up with
NULLvalue for the request’s currency and pre-configuredstore + pricemode. Amend your custom calls to ShipmentFacadeInterface::getAvailableMethods method accordingly to your requirements.
- Does not populate
CheckoutAvailableShipmentMethodsPlugin is an adapter to ShipmentFacadeInterface::getAvailableMethods. If you use this plugin, you will need to amend its usage in your code.
- The
MethodForm::setDefaultOptionsdeprecated method was removed, useMethodForm::configureOptionsinstead. - The
ShipmentDependencyProvider::STOREstatic dependency access was replaced with properStoreFacadeInterfacebridged access. Amend your implementation if you have customized it. - Note: The
MethodForm.defaultPriceform field was replaced with its multi-currency representation. Amend your implementation if you have customized it. - Note: The
MethodFormform now works on theShipmentMethodtransfer object instead of simple array. Amend your implementation if you have customized it. - Note: The
ShipmentMethodDeliveryTimePluginInterfaceinterface now expects the returned delivery time in seconds. Amend your implementations of this plugin accordingly. The DemoShop example implementation of the plugin and its usage in theShipmentFormDataProvider::getDeliveryTimemethod are also updated.
Go to the Shipment management Back Office to verify your shipment method prices.
Upgrading from version 4.* to version 5.*
In version 5, shipment lost the direct foreign key sales.fk_shipment_method to the sales_order table, it was replaced with the spy_sales_shipment table where all shipment information is stored.
A new SalesOrderHydration plugin was added to populate OrderTransfer with shipment information ShipmentOrderHydratePlugin.
The new shipment table structure requires manual data migration, we have provided migration script, you can read how to migrate shipment data in Migration Guide Sales.
Upgrading from version 2.* to version 3.*
The tax plugins are using the version 3.* of the Tax module. You need to upgrade the Tax module.
Thank you!
For submitting the form