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
Shipment
module 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
Shipment
module 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, please 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/shipment
module 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\Persistence
namespace. It’s important that you make sure that they extend the base classes from the Spryker core, e.g.:\Orm\Zed\Shipment\Persistence\SpyShipmentMethodPrice
extends\Spryker\Zed\Shipment\Persistence\Propel\AbstractSpyShipmentMethodPrice
\Orm\Zed\Shipment\Persistence\SpyShipmentMethodPriceQuery
extends\Spryker\Zed\Shipment\Persistence\Propel\AbstractSpyShipmentMethodPriceQuery
- Install the database changes by running
-
Run
vendor/bin/console transfer:generate
to update and generate transfer object changes.Transfer object changes
Property
defaultPrice
inShipmentMethod
transfer object is replaced by prices, andstoreCurrencyPrice
properties.prices transfer
object property contains the shipment method related prices fromspy_shipment_method_price
database table as aMoneyValue
transfer object collection.storeCurrencyPrice
transfer object property contains 1 specific price, based on the pre-configuredstore + price
mode and for the requested currency.
ShipmentMethod
transfer object now contains a shipmentMethodKey property, accordingly to the new database structure. -
Replace the usages of
ShipmentMethod.defaultPrice
transfer object property in your custom codes, depending on your requirements. -
Migrate your old database structure by creating a
spy_shipment_method_price
row for each of yourspy_shipment_method
rows. The number of thespy_shipment_method_price
rows per shipment method should match the number of store and currency pair set up in the configuration. Depending on your requirements, you can setgross/net
prices as0
/null
/any integer
value 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
MoneyCollectFormType
form 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::createMethod
method now expects “prices”MoneyValue
transfer object collection to be set in the providedShipmentMethod
transfer object. Update your custom calls to this method accordingly. - The
ShipmentFacadeInterface::updateMethod
method now expects “prices”MoneyValue
transfer object collection to be set in the providedShipmentMethod
transfer object. Update your custom calls to this method accordingly. - The
ShipmentFacadeInterface::getAvailableMethods
method applies multi-currency feature:- Does not populate
taxRate
transfer object property anymore in shipment method transfer objects. - Excludes shipment methods which would end up with
NULL
value for the request’s currency and pre-configuredstore + price
mode. 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::setDefaultOptions
deprecated method was removed, useMethodForm::configureOptions
instead. - The
ShipmentDependencyProvider::STORE
static dependency access was replaced with properStoreFacadeInterface
bridged access. Amend your implementation if you have customized it. - Note: The
MethodForm.defaultPrice
form field was replaced with its multi-currency representation. Amend your implementation if you have customized it. - Note: The
MethodForm
form now works on theShipmentMethod
transfer object instead of simple array. Amend your implementation if you have customized it. - Note: The
ShipmentMethodDeliveryTimePluginInterface
interface 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::getDeliveryTime
method 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