commerce_shipping-8.x-2.0-rc2/src/Entity/Shipment.php
src/Entity/Shipment.php
<?php namespace Drupal\commerce_shipping\Entity; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityChangedTrait; use Drupal\Core\Entity\EntityMalformedException; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\commerce_order\Adjustment; use Drupal\commerce_price\Calculator; use Drupal\commerce_price\Price; use Drupal\commerce_shipping\Plugin\Commerce\PackageType\PackageTypeInterface as PackageTypePluginInterface; use Drupal\commerce_shipping\ProposedShipment; use Drupal\commerce_shipping\ShipmentItem; use Drupal\physical\Weight; use Drupal\profile\Entity\ProfileInterface; /** * Defines the shipment entity class. * * @ContentEntityType( * id = "commerce_shipment", * label = @Translation("Shipment"), * label_collection = @Translation("Shipments"), * label_singular = @Translation("shipment"), * label_plural = @Translation("shipments"), * label_count = @PluralTranslation( * singular = "@count shipment", * plural = "@count shipments", * ), * bundle_label = @Translation("Shipment type"), * handlers = { * "event" = "Drupal\commerce_shipping\Event\ShipmentEvent", * "list_builder" = "Drupal\commerce_shipping\ShipmentListBuilder", * "storage" = "Drupal\commerce_shipping\ShipmentStorage", * "access" = "Drupal\commerce_shipping\ShipmentAccessControlHandler", * "permission_provider" = "Drupal\commerce_shipping\ShipmentPermissionProvider", * "views_data" = "Drupal\commerce_shipping\ShipmentViewsData", * "form" = { * "default" = "Drupal\commerce_shipping\Form\ShipmentForm", * "add" = "Drupal\commerce_shipping\Form\ShipmentForm", * "edit" = "Drupal\commerce_shipping\Form\ShipmentForm", * "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm", * "resend-confirmation" = "Drupal\commerce_shipping\Form\ShipmentConfirmationResendForm", * }, * "inline_form" = "Drupal\commerce_shipping\Form\ShipmentInlineForm", * "route_provider" = { * "default" = "Drupal\commerce_shipping\ShipmentRouteProvider", * }, * }, * base_table = "commerce_shipment", * admin_permission = "administer commerce_shipment", * fieldable = TRUE, * entity_keys = { * "id" = "shipment_id", * "bundle" = "type", * "label" = "title", * "uuid" = "uuid", * }, * links = { * "canonical" = "/admin/commerce/orders/{commerce_order}/shipments/{commerce_shipment}", * "add-page" = "/admin/commerce/orders/{commerce_order}/shipments/add", * "collection" = "/admin/commerce/orders/{commerce_order}/shipments", * "add-form" = "/admin/commerce/orders/{commerce_order}/shipments/add/{commerce_shipment_type}", * "edit-form" = "/admin/commerce/orders/{commerce_order}/shipments/{commerce_shipment}/edit", * "delete-form" = "/admin/commerce/orders/{commerce_order}/shipments/{commerce_shipment}/delete", * "resend-confirmation-form" = "/admin/commerce/orders/{commerce_order}/shipments/{commerce_shipment}/resend-confirmation", * "state-transition-form" = "/admin/commerce/orders/{commerce_order}/shipments/{commerce_shipment}/{field_name}/{transition_id}" * }, * bundle_entity_type = "commerce_shipment_type", * field_ui_base_route = "entity.commerce_shipment_type.edit_form", * ) */ class Shipment extends ContentEntityBase implements ShipmentInterface { use EntityChangedTrait; /** * {@inheritdoc} */ protected function urlRouteParameters($rel) { $uri_route_parameters = parent::urlRouteParameters($rel); $uri_route_parameters['commerce_order'] = $this->getOrderId(); return $uri_route_parameters; } /** * {@inheritdoc} */ public function clearRate() { $fields = ['amount', 'original_amount', 'shipping_method', 'shipping_service']; foreach ($fields as $field) { $this->set($field, NULL); } return $this; } /** * {@inheritdoc} */ public function populateFromProposedShipment(ProposedShipment $proposed_shipment) { if ($proposed_shipment->getType() != $this->bundle()) { throw new \InvalidArgumentException(sprintf('The proposed shipment type "%s" does not match the shipment type "%s".', $proposed_shipment->getType(), $this->bundle())); } $this->set('order_id', $proposed_shipment->getOrderId()); $this->set('title', $proposed_shipment->getTitle()); $this->set('items', $proposed_shipment->getItems()); $this->set('shipping_profile', $proposed_shipment->getShippingProfile()); $this->set('package_type', $proposed_shipment->getPackageTypeId()); foreach ($proposed_shipment->getCustomFields() as $field_name => $value) { if ($this->hasField($field_name)) { $this->set($field_name, $value); } else { $this->setData($field_name, $value); } } $this->prepareFields(); } /** * {@inheritdoc} */ public function getOrder() { return $this->get('order_id')->entity; } /** * {@inheritdoc} */ public function getOrderId() { return $this->get('order_id')->target_id; } /** * {@inheritdoc} */ public function getPackageType() { if (!$this->get('package_type')->isEmpty()) { $package_type_id = $this->get('package_type')->value; $package_type_manager = \Drupal::service('plugin.manager.commerce_package_type'); return $package_type_manager->createInstance($package_type_id); } } /** * {@inheritdoc} */ public function setPackageType(PackageTypePluginInterface $package_type) { $this->set('package_type', $package_type->getId()); $this->recalculateWeight(); return $this; } /** * {@inheritdoc} */ public function getShippingMethod() { return $this->get('shipping_method')->entity; } /** * {@inheritdoc} */ public function setShippingMethod(ShippingMethodInterface $shipping_method) { $this->set('shipping_method', $shipping_method); return $this; } /** * {@inheritdoc} */ public function getShippingMethodId() { return $this->get('shipping_method')->target_id; } /** * {@inheritdoc} */ public function setShippingMethodId($shipping_method_id) { $this->set('shipping_method', $shipping_method_id); return $this; } /** * {@inheritdoc} */ public function getShippingService() { return $this->get('shipping_service')->value; } /** * {@inheritdoc} */ public function setShippingService($shipping_service) { $this->set('shipping_service', $shipping_service); return $this; } /** * {@inheritdoc} */ public function getShippingProfile() { return $this->get('shipping_profile')->entity; } /** * {@inheritdoc} */ public function setShippingProfile(ProfileInterface $profile) { $this->set('shipping_profile', $profile); return $this; } /** * {@inheritdoc} */ public function getTitle() { return $this->get('title')->value; } /** * {@inheritdoc} */ public function setTitle($title) { $this->set('title', $title); return $this; } /** * {@inheritdoc} */ public function getItems() { return $this->get('items')->getShipmentItems(); } /** * {@inheritdoc} */ public function setItems(array $shipment_items) { $this->set('items', $shipment_items); $this->recalculateWeight(); return $this; } /** * {@inheritdoc} */ public function getTotalQuantity() { $total_quantity = '0'; foreach ($this->getItems() as $item) { $total_quantity = Calculator::add($total_quantity, $item->getQuantity()); } return $total_quantity; } /** * {@inheritdoc} */ public function hasItems() { return !$this->get('items')->isEmpty(); } /** * {@inheritdoc} */ public function addItem(ShipmentItem $shipment_item) { $this->get('items')->appendItem($shipment_item); $this->recalculateWeight(); return $this; } /** * {@inheritdoc} */ public function removeItem(ShipmentItem $shipment_item) { $this->get('items')->removeShipmentItem($shipment_item); $this->recalculateWeight(); return $this; } /** * {@inheritdoc} */ public function getTotalDeclaredValue() { $total_declared_value = NULL; foreach ($this->getItems() as $item) { $declared_value = $item->getDeclaredValue(); $total_declared_value = $total_declared_value ? $total_declared_value->add($declared_value) : $declared_value; } return $total_declared_value; } /** * {@inheritdoc} */ public function getWeight() { if (!$this->get('weight')->isEmpty()) { return $this->get('weight')->first()->toMeasurement(); } } /** * {@inheritdoc} */ public function setWeight(Weight $weight) { $this->set('weight', $weight); return $this; } /** * {@inheritdoc} */ public function getOriginalAmount() { if (!$this->get('original_amount')->isEmpty()) { return $this->get('original_amount')->first()->toPrice(); } } /** * {@inheritdoc} */ public function setOriginalAmount(Price $original_amount) { $this->set('original_amount', $original_amount); return $this; } /** * {@inheritdoc} */ public function getAmount() { if (!$this->get('amount')->isEmpty()) { return $this->get('amount')->first()->toPrice(); } } /** * {@inheritdoc} */ public function setAmount(Price $amount) { $this->set('amount', $amount); return $this; } /** * {@inheritdoc} */ public function getAdjustedAmount(array $adjustment_types = []) { $amount = $this->getAmount(); if (!$amount) { return NULL; } foreach ($this->getAdjustments($adjustment_types) as $adjustment) { if (!$adjustment->isIncluded()) { $amount = $amount->add($adjustment->getAmount()); } } $rounder = \Drupal::service('commerce_price.rounder'); $amount = $rounder->round($amount); return $amount; } /** * {@inheritdoc} */ public function getAdjustments(array $adjustment_types = []) { /** @var \Drupal\commerce_order\Adjustment[] $adjustments */ $adjustments = $this->get('adjustments')->getAdjustments(); // Filter adjustments by type, if needed. if ($adjustment_types) { foreach ($adjustments as $index => $adjustment) { if (!in_array($adjustment->getType(), $adjustment_types)) { unset($adjustments[$index]); } } $adjustments = array_values($adjustments); } return $adjustments; } /** * {@inheritdoc} */ public function setAdjustments(array $adjustments) { $this->set('adjustments', $adjustments); return $this; } /** * {@inheritdoc} */ public function addAdjustment(Adjustment $adjustment) { $this->get('adjustments')->appendItem($adjustment); return $this; } /** * {@inheritdoc} */ public function removeAdjustment(Adjustment $adjustment) { $this->get('adjustments')->removeAdjustment($adjustment); return $this; } /** * {@inheritdoc} */ public function clearAdjustments() { $locked_callback = function ($adjustment) { /** @var \Drupal\commerce_order\Adjustment $adjustment */ return $adjustment->isLocked(); }; $adjustments = array_filter($this->getAdjustments(), $locked_callback); $this->setAdjustments($adjustments); return $this; } /** * {@inheritdoc} */ public function getTrackingCode() { return $this->get('tracking_code')->value; } /** * {@inheritdoc} */ public function setTrackingCode($tracking_code) { $this->set('tracking_code', $tracking_code); return $this; } /** * {@inheritdoc} */ public function getState() { return $this->get('state')->first(); } /** * {@inheritdoc} */ public function getData($key, $default = NULL) { $data = []; if (!$this->get('data')->isEmpty()) { $data = $this->get('data')->first()->getValue(); } return $data[$key] ?? $default; } /** * {@inheritdoc} */ public function setData($key, $value) { $this->get('data')->__set($key, $value); return $this; } /** * {@inheritdoc} */ public function unsetData($key) { if (!$this->get('data')->isEmpty()) { $data = $this->get('data')->first()->getValue(); unset($data[$key]); $this->set('data', $data); } return $this; } /** * {@inheritdoc} */ public function getCreatedTime() { return $this->get('created')->value; } /** * {@inheritdoc} */ public function setCreatedTime($timestamp) { $this->set('created', $timestamp); return $this; } /** * {@inheritdoc} */ public function getShippedTime() { return $this->get('shipped')->value; } /** * {@inheritdoc} */ public function setShippedTime($timestamp) { $this->set('shipped', $timestamp); return $this; } /** * {@inheritdoc} */ public function preSave(EntityStorageInterface $storage) { parent::preSave($storage); $this->prepareFields(); foreach (['order_id'] as $field) { if ($this->get($field)->isEmpty()) { throw new EntityMalformedException(sprintf('Required shipment field "%s" is empty.', $field)); } } } /** * {@inheritdoc} */ public static function postDelete(EntityStorageInterface $storage, array $entities) { parent::postDelete($storage, $entities); $profiles_to_delete = []; // Delete the shipping profiles referenced by the shipments being deleted. /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */ foreach ($entities as $shipment) { $shipping_profile = $shipment->getShippingProfile(); if ($shipping_profile && !$shipping_profile->getOwnerId()) { $profiles_to_delete[] = $shipping_profile; } } if ($profiles_to_delete) { /** @var \Drupal\profile\ProfileStorageInterface $profile_storage */ $profile_storage = \Drupal::service('entity_type.manager')->getStorage('profile'); $profile_storage->delete($profiles_to_delete); } } /** * Ensures that the package_type and weight fields are populated. */ protected function prepareFields() { if (empty($this->getPackageType()) && !empty($this->getShippingMethodId())) { $shipping_method = $this->getShippingMethod(); if ($shipping_method) { $default_package_type = $shipping_method->getPlugin()->getDefaultPackageType(); $this->set('package_type', $default_package_type->getId()); } } $this->recalculateWeight(); } /** * Recalculates the shipment's weight. */ protected function recalculateWeight() { if (!$this->hasItems()) { // Can't calculate the weight if the items are still unavailable. return; } /** @var \Drupal\physical\Weight $weight */ $weight = NULL; foreach ($this->getItems() as $shipment_item) { $shipment_item_weight = $shipment_item->getWeight(); $weight = $weight ? $weight->add($shipment_item_weight) : $shipment_item_weight; } if ($package_type = $this->getPackageType()) { $package_type_weight = $package_type->getWeight(); $weight = $weight->add($package_type_weight); } $this->setWeight($weight); } /** * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields = parent::baseFieldDefinitions($entity_type); // The order backreference. $fields['order_id'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Order')) ->setDescription(t('The parent order.')) ->setSetting('target_type', 'commerce_order') ->setRequired(TRUE) ->setReadOnly(TRUE); $fields['package_type'] = BaseFieldDefinition::create('string') ->setLabel(t('Package type')) ->setDescription(t('The package type.')) ->setRequired(TRUE) ->setDefaultValue('') ->setSetting('max_length', 255) ->setDisplayConfigurable('view', TRUE); $fields['shipping_method'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Shipping method')) ->setRequired(TRUE) ->setSetting('target_type', 'commerce_shipping_method') ->setSetting('display_description', TRUE) ->setDisplayOptions('form', [ 'type' => 'commerce_shipping_rate', 'weight' => 0, ]) ->setDisplayOptions('view', [ 'label' => 'above', 'type' => 'commerce_shipping_method', 'weight' => 0, ]) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); $fields['shipping_service'] = BaseFieldDefinition::create('string') ->setLabel(t('Shipping service')) ->setRequired(TRUE) ->setDescription(t('The shipping service.')) ->setDefaultValue('') ->setSetting('max_length', 255); $fields['shipping_profile'] = BaseFieldDefinition::create('entity_reference_revisions') ->setLabel(t('Shipping information')) ->setRequired(TRUE) ->setSetting('target_type', 'profile') ->setSetting('handler', 'default') ->setSetting('handler_settings', ['target_bundles' => ['customer' => 'customer']]) ->setDisplayOptions('form', [ 'type' => 'commerce_shipping_profile', 'weight' => -10, 'settings' => [], ]) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); $fields['title'] = BaseFieldDefinition::create('string') ->setLabel(t('Title')) ->setDescription(t('The shipment title.')) ->setRequired(TRUE) ->setSettings([ 'default_value' => '', 'max_length' => 255, ]) ->setDisplayOptions('form', [ 'type' => 'string_textfield', 'weight' => -20, ]) ->setDisplayConfigurable('view', TRUE) ->setDisplayConfigurable('form', TRUE); $fields['items'] = BaseFieldDefinition::create('commerce_shipment_item') ->setLabel(t('Items')) ->setRequired(TRUE) ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', TRUE); $fields['weight'] = BaseFieldDefinition::create('physical_measurement') ->setLabel(t('Weight', [], ['context' => 'physical'])) ->setRequired(TRUE) ->setSetting('measurement_type', 'weight') ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); $fields['original_amount'] = BaseFieldDefinition::create('commerce_price') ->setLabel(t('Original amount')) ->setDescription(t('The original amount.')) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', TRUE); $fields['amount'] = BaseFieldDefinition::create('commerce_price') ->setLabel(t('Amount')) ->setDescription(t('The amount.')) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', TRUE); $fields['adjustments'] = BaseFieldDefinition::create('commerce_adjustment') ->setLabel(t('Adjustments')) ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', FALSE); $fields['tracking_code'] = BaseFieldDefinition::create('string') ->setLabel(t('Tracking code')) ->setDescription(t('The shipment tracking code.')) ->setDefaultValue('') ->setSetting('max_length', 255) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE) ->setDisplayOptions('form', [ 'type' => 'string_textfield', 'weight' => 50, ]); $fields['state'] = BaseFieldDefinition::create('state') ->setLabel(t('State')) ->setDescription(t('The shipment state.')) ->setRequired(TRUE) ->setDisplayOptions('view', [ 'label' => 'hidden', 'type' => 'state_transition_form', 'settings' => [ 'require_confirmation' => TRUE, 'use_modal' => TRUE, ], 'weight' => 10, ]) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE) ->setSetting('workflow_callback', ['\Drupal\commerce_shipping\Entity\Shipment', 'getWorkflowId']); $fields['data'] = BaseFieldDefinition::create('map') ->setLabel(t('Data')) ->setDescription(t('A serialized array of additional data.')); $fields['created'] = BaseFieldDefinition::create('created') ->setLabel(t('Created')) ->setDescription(t('The time when the shipment was created.')) ->setRequired(TRUE); $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) ->setDescription(t('The time when the shipment was last updated.')) ->setRequired(TRUE); $fields['shipped'] = BaseFieldDefinition::create('timestamp') ->setLabel(t('Shipped')) ->setDescription(t('The time when the shipment was shipped.')); return $fields; } /** * {@inheritdoc} */ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { $shipment_type = ShipmentType::load($bundle); if (!$shipment_type) { throw new \RuntimeException(sprintf('Could not load the "%s" shipment type.', $bundle)); } $fields = []; $fields['shipping_profile'] = clone $base_field_definitions['shipping_profile']; $fields['shipping_profile']->setSetting('handler_settings', [ 'target_bundles' => [ $shipment_type->getProfileTypeId() => $shipment_type->getProfileTypeId(), ], ]); return $fields; } /** * Gets the workflow ID for the state field. * * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment * The shipment. * * @return string * The workflow ID. */ public static function getWorkflowId(ShipmentInterface $shipment) { if (!empty($shipping_method = $shipment->get('shipping_method')->entity)) { if (!empty($plugin = $shipping_method->getPlugin())) { return $plugin->getWorkflowId(); } } return 'shipment_default'; } }