class Shipping in Commerce Shipping 8.2
Provides the Shipping tax type.
Plugin annotation
@CommerceTaxType(
id = "shipping",
label = "Shipping",
weight = 10,
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\commerce_tax\Plugin\Commerce\TaxType\TaxTypeBase implements TaxTypeInterface, ContainerFactoryPluginInterface
- class \Drupal\commerce_shipping\Plugin\Commerce\TaxType\Shipping
- class \Drupal\commerce_tax\Plugin\Commerce\TaxType\TaxTypeBase implements TaxTypeInterface, ContainerFactoryPluginInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Shipping
8 string references to 'Shipping'
- commerce_checkout.commerce_checkout_flow.shipping.yml in config/
optional/ commerce_checkout.commerce_checkout_flow.shipping.yml - config/optional/commerce_checkout.commerce_checkout_flow.shipping.yml
- commerce_shipping.commerce_adjustment_types.yml in ./
commerce_shipping.commerce_adjustment_types.yml - commerce_shipping.commerce_adjustment_types.yml
- commerce_shipping.links.menu.yml in ./
commerce_shipping.links.menu.yml - commerce_shipping.links.menu.yml
- commerce_shipping.routing.yml in ./
commerce_shipping.routing.yml - commerce_shipping.routing.yml
- core.entity_form_mode.profile.shipping.yml in config/
install/ core.entity_form_mode.profile.shipping.yml - config/install/core.entity_form_mode.profile.shipping.yml
File
- src/
Plugin/ Commerce/ TaxType/ Shipping.php, line 28
Namespace
Drupal\commerce_shipping\Plugin\Commerce\TaxTypeView source
class Shipping extends TaxTypeBase {
/**
* The entity UUID mapper.
*
* @var \Drupal\commerce\EntityUuidMapperInterface
*/
protected $entityUuidMapper;
/**
* The rounder.
*
* @var \Drupal\commerce_price\RounderInterface
*/
protected $rounder;
/**
* The shipping order manager.
*
* @var \Drupal\commerce_shipping\ShippingOrderManagerInterface
*/
protected $shippingOrderManager;
/**
* Constructs a new Shipping object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param \Drupal\commerce\EntityUuidMapperInterface $entity_uuid_mapper
* The entity UUID mapper.
* @param \Drupal\commerce_price\RounderInterface $rounder
* The rounder.
* @param \Drupal\commerce_shipping\ShippingOrderManagerInterface $shipping_order_manager
* The shipping order manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, EntityUuidMapperInterface $entity_uuid_mapper, RounderInterface $rounder, ShippingOrderManagerInterface $shipping_order_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $event_dispatcher);
$this->entityUuidMapper = $entity_uuid_mapper;
$this->rounder = $rounder;
$this->shippingOrderManager = $shipping_order_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('entity_type.manager'), $container
->get('event_dispatcher'), $container
->get('commerce.entity_uuid_mapper'), $container
->get('commerce_price.rounder'), $container
->get('commerce_shipping.order_manager'));
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'strategy' => 'default',
// The store UUIDs.
'store_filter' => 'none',
'stores' => [],
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['strategy'] = [
'#type' => 'radios',
'#title' => t('Strategy'),
'#options' => [
'default' => $this
->t("Apply the default (standard) rate of the order's tax type"),
'highest' => $this
->t('Apply the highest rate found on the order'),
'proportional' => $this
->t("Apply each order item's rate proportionally"),
],
'#default_value' => $this->configuration['strategy'],
];
$store_ids = NULL;
if ($this->configuration['stores']) {
$store_ids = $this->entityUuidMapper
->mapToIds('commerce_store', $this->configuration['stores']);
}
$radio_parents = array_merge($form['#parents'], [
'store_filter',
]);
$radio_path = array_shift($radio_parents);
$radio_path .= '[' . implode('][', $radio_parents) . ']';
$form['store_filter'] = [
'#type' => 'radios',
'#title' => $this
->t('Applies to'),
'#default_value' => $this->configuration['store_filter'],
'#options' => [
'none' => $this
->t('All stores'),
'include' => $this
->t('Only the selected stores'),
'exclude' => $this
->t('All except the selected stores'),
],
];
$form['container'] = [
'#type' => 'container',
'#states' => [
'invisible' => [
':input[name="' . $radio_path . '"]' => [
'value' => 'none',
],
],
],
];
$form['container']['stores'] = [
'#parents' => array_merge($form['#parents'], [
'stores',
]),
'#type' => 'commerce_entity_select',
'#title' => $this
->t('Stores'),
'#default_value' => $store_ids,
'#target_type' => 'commerce_store',
'#hide_single_entity' => FALSE,
'#multiple' => TRUE,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
if (!$form_state
->getErrors()) {
$values = $form_state
->getValue($form['#parents']);
$this->configuration = [];
$this->configuration['strategy'] = $values['strategy'];
$this->configuration['store_filter'] = $values['store_filter'];
$this->configuration['stores'] = [];
if ($values['store_filter'] != 'none') {
$this->configuration['stores'] = $this->entityUuidMapper
->mapFromIds('commerce_store', $values['stores']);
}
}
}
/**
* {@inheritdoc}
*/
public function applies(OrderInterface $order) {
if (!$this->shippingOrderManager
->isShippable($order)) {
return FALSE;
}
/** @var \Drupal\commerce_shipping\Entity\ShipmentInterface[] $shipments */
$shipments = $order
->get('shipments')
->referencedEntities();
if (empty($shipments)) {
return FALSE;
}
$store_filter = $this->configuration['store_filter'];
if ($store_filter != 'none') {
$match = in_array($order
->getStore()
->uuid(), $this->configuration['stores']);
$match = $store_filter == 'include' ? $match : !$match;
if (!$match) {
return FALSE;
}
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function apply(OrderInterface $order) {
$tax_adjustments = $order
->collectAdjustments([
'tax',
]);
// Filter-out adjustments with an unknown percentage or source ID,
// usually indicative of a remote tax type.
$tax_adjustments = array_filter($tax_adjustments, function (Adjustment $adjustment) {
$percentage = $adjustment
->getPercentage();
$source_id = $adjustment
->getSourceId();
return isset($percentage) && substr_count($source_id, '|') === 2;
});
if (empty($tax_adjustments)) {
return;
}
if ($this->configuration['strategy'] == 'default') {
$this
->applyDefault($order, $tax_adjustments);
}
elseif ($this->configuration['strategy'] == 'highest') {
$this
->applyHighest($order, $tax_adjustments);
}
elseif ($this->configuration['strategy'] == 'proportional') {
$this
->applyProportional($order, $tax_adjustments);
}
}
/**
* Applies the default tax rate of the order's tax type.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order.
* @param \Drupal\commerce_order\Adjustment[] $tax_adjustments
* The tax adjustments.
*/
protected function applyDefault(OrderInterface $order, array $tax_adjustments) {
// Assume that all tax adjustments have the same tax type and zone ID.
$tax_adjustment = reset($tax_adjustments);
list($tax_type_id, $zone_id, $rate_id) = explode('|', $tax_adjustment
->getSourceId());
$tax_type_storage = $this->entityTypeManager
->getStorage('commerce_tax_type');
/** @var \Drupal\commerce_tax\Entity\TaxTypeInterface $tax_type */
$tax_type = $tax_type_storage
->load($tax_type_id);
if (!$tax_type) {
return;
}
/** @var \Drupal\commerce_tax\Plugin\Commerce\TaxType\LocalTaxTypeInterface $tax_type_plugin */
$tax_type_plugin = $tax_type
->getPlugin();
if (!$tax_type_plugin instanceof LocalTaxTypeInterface) {
return;
}
$zones = $tax_type_plugin
->getZones();
$zone = $zones[$zone_id];
$default_rate = $zone
->getDefaultRate();
$percentage = $default_rate
->getPercentage($order
->getCalculationDate());
foreach ($this
->getShipments($order) as $shipment) {
$display_inclusive = $tax_type_plugin
->isDisplayInclusive();
$tax_amount = $this
->calculateTaxAmount($shipment, $percentage
->getNumber(), $display_inclusive);
$tax_amount = $this->rounder
->round($tax_amount);
$shipment
->addAdjustment(new Adjustment([
'type' => 'tax',
'label' => $zone
->getDisplayLabel(),
'amount' => $tax_amount,
'percentage' => $percentage
->getNumber(),
'source_id' => $tax_type
->id() . '|' . $zone
->getId() . '|' . $default_rate
->getId(),
'included' => $display_inclusive,
]));
}
}
/**
* Applies the highest tax rate found on the order.
*
* If an order has one order item taxed using the standard rate (e.g. 20%)
* and one taxed using the intermediate rate (e.g. 15%), then the standard
* rate will be applied, just like with applyDefault().
*
* However, if the order only has an order item taxed using the intermediate
* rate, then the intermediate rate will be applied.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order.
* @param \Drupal\commerce_order\Adjustment[] $tax_adjustments
* The tax adjustments.
*/
protected function applyHighest(OrderInterface $order, array $tax_adjustments) {
/** @var \Drupal\commerce_order\Adjustment[] $tax_adjustments_by_source */
$tax_adjustments_by_source = [];
foreach ($tax_adjustments as $adjustment) {
$tax_adjustments_by_source[$adjustment
->getSourceId()] = $adjustment;
}
// Sort by percentage descending.
uasort($tax_adjustments_by_source, function (Adjustment $a, Adjustment $b) {
return $b
->getPercentage() <=> $a
->getPercentage();
});
$highest_adjustment = reset($tax_adjustments_by_source);
foreach ($this
->getShipments($order) as $shipment) {
$display_inclusive = $highest_adjustment
->isIncluded();
$percentage = $highest_adjustment
->getPercentage();
$tax_amount = $this
->calculateTaxAmount($shipment, $percentage, $display_inclusive);
$tax_amount = $this->rounder
->round($tax_amount);
$definition = [
'amount' => $tax_amount,
] + $highest_adjustment
->toArray();
$shipment
->addAdjustment(new Adjustment($definition));
}
}
/**
* Applies each order item's tax rate proportionally.
*
* Logic:
* 1. Order items are grouped by their tax rates and then summed up.
* 2. Each group's ratio of the subtotal is calculated.
* 3. Each group's tax rate is applied to the shipments, multiplied by
* the ratio and then rounded.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order.
* @param \Drupal\commerce_order\Adjustment[] $tax_adjustments
* The tax adjustments.
*/
protected function applyProportional(OrderInterface $order, array $tax_adjustments) {
if (count($tax_adjustments) === 1) {
$this
->applyHighest($order, $tax_adjustments);
return;
}
// Group order items by tax percentage.
$groups = [];
foreach ($order
->getItems() as $order_item) {
$order_item_total = $order_item
->getTotalPrice();
$order_item_tax_adjustments = $order_item
->getAdjustments([
'tax',
]);
$order_item_tax_adjustment = reset($order_item_tax_adjustments);
$percentage = $order_item_tax_adjustment
->getPercentage();
if (!isset($groups[$percentage])) {
$groups[$percentage] = [
'order_item_total' => $order_item_total,
'tax_adjustment' => $order_item_tax_adjustment,
];
}
else {
$previous_total = $groups[$percentage]['order_item_total'];
$previous_adjustment = $groups[$percentage]['tax_adjustment'];
$groups[$percentage]['order_item_total'] = $previous_total
->add($order_item_total);
$groups[$percentage]['tax_adjustment'] = $previous_adjustment
->add($order_item_tax_adjustment);
}
}
// Sort by percentage descending.
krsort($groups, SORT_NUMERIC);
// Calculate the ratio of each group.
$subtotal = $order
->getSubtotalPrice()
->getNumber();
foreach ($groups as $percentage => $group) {
$order_item_total = $group['order_item_total'];
// If the order item total is zero, the group ratio cannot be
// properly calculated.
if ($order_item_total
->isZero()) {
$groups[$percentage]['ratio'] = '0';
continue;
}
$groups[$percentage]['ratio'] = $order_item_total
->divide($subtotal)
->getNumber();
}
foreach ($this
->getShipments($order) as $shipment) {
foreach ($groups as $percentage => $group) {
$existing_adjustment = $group['tax_adjustment'];
$display_inclusive = $existing_adjustment
->isIncluded();
$tax_amount = $this
->calculateTaxAmount($shipment, $percentage, $display_inclusive);
$tax_amount = $tax_amount
->multiply($group['ratio']);
$tax_amount = $this->rounder
->round($tax_amount);
$definition = [
'amount' => $tax_amount,
] + $existing_adjustment
->toArray();
$shipment
->addAdjustment(new Adjustment($definition));
}
}
}
/**
* Gets the order's shipments.
*
* Filters out shipments which are still incomplete (no rate selected).
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order.
*
* @return \Drupal\commerce_shipping\Entity\ShipmentInterface[]
* The shipments.
*/
protected function getShipments(OrderInterface $order) {
/** @var \Drupal\commerce_shipping\Entity\Shipment[] $shipments */
$shipments = $order
->get('shipments')
->referencedEntities();
$shipments = array_filter($shipments, function (ShipmentInterface $shipment) {
return $shipment
->getShippingMethodId() && $shipment
->getAmount();
});
return $shipments;
}
/**
* Calculates the tax amount for the given shipment.
*
* @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
* The shipment.
* @param string $percentage
* The tax rate percentage.
* @param bool $included
* Whether tax is already included in the price.
*
* @return \Drupal\commerce_price\Price
* The unrounded tax amount.
*/
protected function calculateTaxAmount(ShipmentInterface $shipment, $percentage, $included = FALSE) {
$shipment_amount = $shipment
->getAdjustedAmount([
'shipping_promotion',
]);
$tax_amount = $shipment_amount
->multiply($percentage);
if ($included) {
$divisor = Calculator::add('1', $percentage);
$tax_amount = $tax_amount
->divide($divisor);
}
return $tax_amount;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
Shipping:: |
protected | property | The entity UUID mapper. | |
Shipping:: |
protected | property | The rounder. | |
Shipping:: |
protected | property | The shipping order manager. | |
Shipping:: |
public | function |
Checks whether the tax type applies to the given order. Overrides TaxTypeBase:: |
|
Shipping:: |
public | function |
Applies the tax type to the given order. Overrides TaxTypeInterface:: |
|
Shipping:: |
protected | function | Applies the default tax rate of the order's tax type. | |
Shipping:: |
protected | function | Applies the highest tax rate found on the order. | |
Shipping:: |
protected | function | Applies each order item's tax rate proportionally. | |
Shipping:: |
public | function |
Form constructor. Overrides TaxTypeBase:: |
|
Shipping:: |
protected | function | Calculates the tax amount for the given shipment. | |
Shipping:: |
public static | function |
Creates an instance of the plugin. Overrides TaxTypeBase:: |
|
Shipping:: |
public | function |
Gets default configuration for this plugin. Overrides TaxTypeBase:: |
|
Shipping:: |
protected | function | Gets the order's shipments. | |
Shipping:: |
public | function |
Form submission handler. Overrides TaxTypeBase:: |
|
Shipping:: |
public | function |
Constructs a new Shipping object. Overrides TaxTypeBase:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TaxTypeBase:: |
protected | property | The ID of the parent config entity. | |
TaxTypeBase:: |
protected | property | The entity type manager. | |
TaxTypeBase:: |
protected | property | The event dispatcher. | |
TaxTypeBase:: |
protected | property | The parent config entity. | |
TaxTypeBase:: |
protected | property | A cache of prepared customer profiles, keyed by order ID. | |
TaxTypeBase:: |
protected | function | Builds a customer profile for the given order. | |
TaxTypeBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
|
TaxTypeBase:: |
public | function |
Gets this plugin's configuration. Overrides ConfigurableInterface:: |
|
TaxTypeBase:: |
public | function |
Gets the tax type label. Overrides TaxTypeInterface:: |
|
TaxTypeBase:: |
protected | function | Gets the taxable type for the given order item. | |
TaxTypeBase:: |
public | function |
Gets the tax type weight. Overrides TaxTypeInterface:: |
|
TaxTypeBase:: |
public | function |
Gets whether the tax type is display inclusive. Overrides TaxTypeInterface:: |
|
TaxTypeBase:: |
protected | function | Resolves the customer profile for the given order item. | |
TaxTypeBase:: |
public | function |
Sets the configuration for this plugin instance. Overrides ConfigurableInterface:: |
1 |
TaxTypeBase:: |
public | function |
Form validation handler. Overrides PluginFormInterface:: |
1 |
TaxTypeBase:: |
public | function |
Overrides DependencySerializationTrait:: |
|
TaxTypeBase:: |
public | function |
Overrides DependencySerializationTrait:: |