View source
<?php
namespace Drupal\commerce_fedex\Plugin\Commerce\ShippingMethod;
use Drupal\address\AddressInterface;
use Drupal\address\Plugin\Field\FieldType\AddressItem;
use Drupal\commerce_fedex\Event\CommerceFedExEvents;
use Drupal\commerce_fedex\Event\RateRequestEvent;
use Drupal\commerce_fedex\FedExAddressResolver;
use Drupal\commerce_fedex\FedExRequestInterface;
use Drupal\commerce_fedex\FedExPluginManager;
use Drupal\commerce_price\Price;
use Drupal\commerce_price\RounderInterface;
use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\commerce_shipping\PackageTypeManagerInterface;
use Drupal\commerce_shipping\Plugin\Commerce\PackageType\PackageTypeInterface;
use Drupal\commerce_shipping\Plugin\Commerce\ShippingMethod\ShippingMethodBase;
use Drupal\commerce_shipping\ShipmentItem;
use Drupal\commerce_shipping\ShippingRate;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
use Drupal\physical\Volume;
use Drupal\physical\VolumeUnit;
use Drupal\physical\Weight as PhysicalWeight;
use Drupal\physical\WeightUnit as PhysicalWeightUnits;
use Drupal\physical\LengthUnit as PhysicalLengthUnits;
use Drupal\Core\Form\FormStateInterface;
use Drupal\state_machine\WorkflowManagerInterface;
use NicholasCreativeMedia\FedExPHP\Enums\DropoffType;
use NicholasCreativeMedia\FedExPHP\Enums\LinearUnits;
use NicholasCreativeMedia\FedExPHP\Enums\PhysicalPackagingType;
use NicholasCreativeMedia\FedExPHP\Enums\RateRequestType;
use NicholasCreativeMedia\FedExPHP\Services\RateService;
use NicholasCreativeMedia\FedExPHP\Structs\Address;
use NicholasCreativeMedia\FedExPHP\Structs\Dimensions;
use NicholasCreativeMedia\FedExPHP\Structs\Money;
use NicholasCreativeMedia\FedExPHP\Structs\Party;
use NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem;
use NicholasCreativeMedia\FedExPHP\Structs\RequestedShipment;
use NicholasCreativeMedia\FedExPHP\Structs\Weight as FedexWeight;
use NicholasCreativeMedia\FedExPHP\Enums\WeightUnits as FedexWeightUnits;
use NicholasCreativeMedia\FedExPHP\Enums\LinearUnits as FedexLengthUnits;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class FedEx extends ShippingMethodBase {
use FedExAddressResolver;
const SHIPPING_TYPE_DOMESTIC = 'domestic';
const SHIPPING_TYPE_INTERNATIONAL = 'intl';
const PACKAGE_ALL_IN_ONE = 'allinone';
const PACKAGE_INDIVIDUAL = 'individual';
const PACKAGE_CALCULATE = 'calculate';
protected $eventDispatcher;
protected $fedExServiceManager;
protected $plugins;
protected $watchdog;
protected $rounder;
protected $fedExRequest;
public function __construct(array $configuration, $plugin_id, $plugin_definition, PackageTypeManagerInterface $package_type_manager, WorkflowManagerInterface $workflow_manager, FedExPluginManager $fedex_service_manager, EventDispatcherInterface $event_dispatcher, FedExRequestInterface $fedex_request, LoggerInterface $watchdog, RounderInterface $rounder) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $package_type_manager, $workflow_manager);
$this->watchdog = $watchdog;
$this->fedExRequest = $fedex_request;
$this->fedExServiceManager = $fedex_service_manager;
$this->eventDispatcher = $event_dispatcher;
$this->rounder = $rounder;
if (empty($this->configuration['plugins'])) {
$this->configuration['plugins'] = [];
}
$this
->updatePlugins()
->getPlugins();
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('plugin.manager.commerce_package_type'), $container
->get('plugin.manager.workflow'), $container
->get('plugin.manager.commerce_fedex_service'), $container
->get('event_dispatcher'), $container
->get('commerce_fedex.fedex_request'), $container
->get('logger.channel.commerce_fedex'), $container
->get('commerce_price.rounder'));
}
public function defaultConfiguration() {
return [
'api_information' => [
'api_key' => '',
'api_password' => '',
'account_number' => '',
'meter_number' => '',
'mode' => 'test',
],
'options' => [
'packaging' => static::PACKAGE_ALL_IN_ONE,
'rate_request_type' => RateRequestType::VALUE_NONE,
'dropoff' => DropoffType::VALUE_REGULAR_PICKUP,
'insurance' => FALSE,
'rate_multiplier' => 1.0,
'round' => PHP_ROUND_HALF_UP,
'log' => [],
],
'plugins' => [],
] + parent::defaultConfiguration();
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
if (empty($this->configuration['services'])) {
$service_ids = array_keys($this->services);
$this->configuration['services'] = array_combine($service_ids, $service_ids);
}
$form['api_information'] = [
'#type' => 'details',
'#title' => $this
->t('API information'),
'#description' => $this
->isConfigured() ? $this
->t('Update your FedEx API information.') : $this
->t('Fill in your FedEx API information.'),
'#weight' => $this
->isConfigured() ? 10 : -10,
'#open' => !$this
->isConfigured(),
];
$form['api_information']['api_key'] = [
'#type' => 'textfield',
'#title' => $this
->t('API key'),
'#description' => $this
->t('Enter your FedEx API key.'),
'#default_value' => $this->configuration['api_information']['api_key'],
];
$form['api_information']['api_password'] = [
'#type' => 'password',
'#title' => $this
->t('API password'),
'#description' => $this
->t('Enter your FedEx API password only if you wish to change its value.'),
'#default_value' => '',
];
$form['api_information']['account_number'] = [
'#type' => 'number',
'#min' => 0,
'#step' => 1,
'#title' => $this
->t('Account number'),
'#description' => $this
->t('Enter your FedEx account number.'),
'#default_value' => $this->configuration['api_information']['account_number'],
];
$form['api_information']['meter_number'] = [
'#type' => 'number',
'#min' => 0,
'#step' => 1,
'#title' => $this
->t('Meter number'),
'#description' => $this
->t('Enter your FedEx meter number.'),
'#default_value' => $this->configuration['api_information']['meter_number'],
];
$form['api_information']['mode'] = [
'#type' => 'select',
'#title' => $this
->t('Mode'),
'#description' => $this
->t('Choose whether to use the test or live mode.'),
'#options' => [
'test' => $this
->t('Test'),
'live' => $this
->t('Live'),
],
'#default_value' => $this->configuration['api_information']['mode'],
];
$form['options'] = [
'#type' => 'details',
'#title' => $this
->t('Fedex Options'),
'#description' => $this
->t('Additional options for Fedex'),
];
$form['options']['packaging'] = [
'#type' => 'select',
'#title' => $this
->t('Packaging strategy'),
'#description' => $this
->t('Select your packaging strategy. "All items in one box" will ignore package type and product dimensions, and assume all items go in one box. "Each item in its own box" will create a box for each line item in the order, "Calculate" Will attempt to figure out how many boxes are needed based on package type volumes and product volumes similar to commerce_fedex 7.x'),
'#options' => [
static::PACKAGE_ALL_IN_ONE => $this
->t("All items in one box"),
static::PACKAGE_INDIVIDUAL => $this
->t("Each item in its own box"),
static::PACKAGE_CALCULATE => $this
->t("Calculate"),
],
'#default_value' => $this->configuration['options']['packaging'],
];
$form['options']['rate_request_type'] = [
'#type' => 'select',
'#title' => $this
->t('Pricing options'),
'#description' => $this
->t('Select the pricing option to use when requesting a rate quote. Note that discounted rates are only available when sending production requests.'),
'#options' => [
RateRequestType::VALUE_LIST => $this
->t('Standard pricing (LIST)'),
RateRequestType::VALUE_NONE => $this
->t("This FedEx account's discounted pricing (ACCOUNT)"),
],
'#default_value' => $this->configuration['options']['rate_request_type'],
];
$form['options']['dropoff'] = [
'#type' => 'select',
'#title' => $this
->t('Dropoff type'),
'#description' => $this
->t('Default dropoff/pickup location for your FedEx shipments'),
'#options' => static::enumToList(DropoffType::getValidValues()),
'#default_value' => $this->configuration['options']['dropoff'],
];
$form['options']['insurance'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Include insurance'),
'#description' => $this
->t('Include insurance value of shippable line items in FedEx rate requests'),
'#default_value' => $this->configuration['options']['insurance'],
];
$form['options']['rate_multiplier'] = [
'#type' => 'number',
'#title' => $this
->t('Rate multiplier'),
'#description' => $this
->t('A number that each rate returned from FedEx will be multiplied by. For example, enter 1.5 to mark up shipping costs to 150%.'),
'#min' => 0.1,
'#step' => 0.1,
'#size' => 5,
'#default_value' => $this->configuration['options']['rate_multiplier'],
];
$form['options']['round'] = [
'#type' => 'select',
'#title' => $this
->t('Round type'),
'#description' => $this
->t('Choose how the shipping rate should be rounded.'),
'#options' => [
PHP_ROUND_HALF_UP => 'Half up',
PHP_ROUND_HALF_DOWN => 'Half down',
PHP_ROUND_HALF_EVEN => 'Half even',
PHP_ROUND_HALF_ODD => 'Half odd',
],
'#default_value' => $this->configuration['options']['round'],
];
$form['options']['log'] = [
'#type' => 'checkboxes',
'#title' => $this
->t('Log the following messages for debugging'),
'#options' => [
'request' => $this
->t('API request messages'),
'response' => $this
->t('API response messages'),
],
'#default_value' => $this->configuration['options']['log'],
];
foreach ($this->fedExServiceManager
->getDefinitions() as $plugin_id => $definition) {
$plugin = $this->plugins
->get($plugin_id);
$subform = [
'#type' => 'details',
'#title' => $definition['options_label']
->render(),
'#description' => $definition['options_description']
->render(),
];
$form[$plugin_id] = $plugin
->buildConfigurationForm($subform, $form_state);
if ($form[$plugin_id] == $subform) {
unset($form[$plugin_id]);
}
}
return $form;
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::validateConfigurationForm($form, $form_state);
foreach ($this->fedExServiceManager
->getDefinitions() as $plugin_id => $definition) {
if (!empty($form[$plugin_id])) {
$plugin = $this->plugins
->get($plugin_id);
$plugin
->validateConfigurationForm($form[$plugin_id], SubformState::createForSubform($form[$plugin_id], $form_state
->getCompleteForm(), $form_state));
}
}
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
if (!$form_state
->getErrors()) {
$values = $form_state
->getValue($form['#parents']);
$this->configuration['api_information']['api_key'] = $values['api_information']['api_key'];
if (!empty($values['api_information']['api_password'])) {
$this->configuration['api_information']['api_password'] = $values['api_information']['api_password'];
}
$this->configuration['api_information']['account_number'] = $values['api_information']['account_number'];
$this->configuration['api_information']['meter_number'] = $values['api_information']['meter_number'];
$this->configuration['api_information']['mode'] = $values['api_information']['mode'];
$this->configuration['options']['packaging'] = $values['options']['packaging'];
$this->configuration['options']['rate_request_type'] = $values['options']['rate_request_type'];
$this->configuration['options']['dropoff'] = $values['options']['dropoff'];
$this->configuration['options']['insurance'] = $values['options']['insurance'];
$this->configuration['options']['rate_multiplier'] = $values['options']['rate_multiplier'];
$this->configuration['options']['round'] = $values['options']['round'];
$this->configuration['options']['log'] = $values['options']['log'];
unset($this->configuration['plugins']);
foreach ($this->fedExServiceManager
->getDefinitions() as $plugin_id => $definition) {
$plugin = $this->plugins
->get($plugin_id);
if (!empty($form[$plugin_id])) {
$plugin
->submitConfigurationForm($form[$plugin_id], SubformState::createForSubform($form[$plugin_id], $form_state
->getCompleteForm(), $form_state));
}
$this->configuration['plugins'][$plugin_id]['configuration'] = $plugin
->getConfiguration();
}
}
parent::submitConfigurationForm($form, $form_state);
}
protected function logRequest($message, $data, $level = LogLevel::INFO, $skip_config_check = FALSE) {
if (is_array($this->configuration['options']['log'])) {
}
if ($skip_config_check || @$this->configuration['options']['log']['request']) {
$this->watchdog
->log($level, "{$message} <br><pre>@rate_request</pre>", [
'@rate_request' => var_export($data, TRUE),
]);
}
}
public function calculateRates(ShipmentInterface $shipment) {
if ($shipment
->getShippingProfile()->address
->isEmpty()) {
return [];
}
if (empty($shipment
->getPackageType())) {
$shipment
->setPackageType($this
->getDefaultPackageType());
}
$rate_service = $this->fedExRequest
->getRateService($this->configuration);
$rate_request = $this
->getRateRequest($rate_service, $shipment);
$this
->logRequest('Sending FedEx request.', $rate_request);
$response = $rate_service
->getRates($rate_request);
$rates = [];
if ($response) {
$this
->logRequest('FedEx response received.', $response);
$allowed_severities = [
'SUCCESS',
'NOTE',
'WARNING',
];
$highest_severity = $response
->getHighestSeverity();
$severity_map = [
'NOTE' => LogLevel::NOTICE,
'WARNING' => LogLevel::WARNING,
'ERROR' => LogLevel::ERROR,
'FAILURE' => LogLevel::ERROR,
];
foreach ($response
->getNotifications() as $notification) {
if (array_key_exists($notification
->getSeverity(), $severity_map)) {
$this->watchdog
->log($severity_map[$notification
->getSeverity()], '@message (Code @code)', [
'@message' => $notification
->getMessage(),
'@code' => $notification
->getCode(),
]);
}
}
if (in_array($highest_severity, $allowed_severities)) {
$multiplier = !empty($this->configuration['options']['rate_multiplier']) ? $this->configuration['options']['rate_multiplier'] : 1.0;
$round = !empty($this->configuration['options']['round']) ? $this->configuration['options']['round'] : PHP_ROUND_HALF_UP;
foreach ($response
->getRateReplyDetails() as $rate_reply_details) {
if (in_array($rate_reply_details
->getServiceType(), array_keys($this
->getServices()))) {
foreach ($rate_reply_details
->getRatedShipmentDetails() as $rated_shipment) {
$rate_details = $rated_shipment
->getShipmentRateDetail();
$rate_type = strpos($rate_details
->getRateType(), 'ACCOUNT') ? RateRequestType::VALUE_NONE : RateRequestType::VALUE_LIST;
if ($this->configuration['options']['rate_request_type'] !== $rate_type) {
continue;
}
$service = $this->services[$rate_reply_details
->getServiceType()];
$cost = $rate_details
->getTotalNetChargeWithDutiesAndTaxes();
$price = new Price((string) $cost
->getAmount(), $cost
->getCurrency());
if ($multiplier != 1) {
$price = $price
->multiply((string) $multiplier);
}
$price = $this->rounder
->round($price, $round);
$rates[] = new ShippingRate([
'shipping_method_id' => $this->parentEntity
->id(),
'service' => $service,
'amount' => $price,
]);
}
}
}
}
}
else {
$this
->logRequest('FedEx sent no response back.', $rate_request, LogLevel::NOTICE, TRUE);
}
return $rates;
}
public function updatePlugins() {
$definitions = $this->fedExServiceManager
->getDefinitions();
$this->configuration['plugins'] = array_intersect_key($this->configuration['plugins'], $definitions);
foreach ($definitions as $plugin_id => $definition) {
if (empty($this->configuration['plugins'][$plugin_id])) {
$this->configuration['plugins'][$plugin_id] = [
'plugin_id' => $plugin_id,
'configuration' => [
'id' => $plugin_id,
],
];
}
}
return $this;
}
public function getPlugins() {
$plugins = [];
foreach ($this->configuration['plugins'] as $plugin_id => $plugin_data) {
$plugins[$plugin_id] = $plugin_data['configuration'];
}
$this->plugins = new DefaultLazyPluginCollection($this->fedExServiceManager, $plugins);
return $this;
}
public static function enumToList(array $enums) {
return array_combine($enums, array_map(function ($d) {
return ucwords(strtolower(str_replace('_', ' ', $d)));
}, $enums));
}
protected function getAddressForFedEx(AddressInterface $address) {
$custom_resolver = 'addressResolve' . $address
->getCountryCode();
if (method_exists($this, $custom_resolver)) {
$party = $this
->{$custom_resolver}($address);
}
else {
$party = new Party();
$party
->setAddress(new Address(array_filter([
$address
->getAddressLine1(),
$address
->getAddressLine2(),
]), $address
->getLocality(), $address
->getAdministrativeArea(), $address
->getPostalCode(), NULL, $address
->getCountryCode(), NULL, FALSE));
}
return $party;
}
protected function getRequestedPackageLineItems(ShipmentInterface $shipment) {
$requested_package_line_items = [];
switch ($this->configuration['options']['packaging']) {
case static::PACKAGE_ALL_IN_ONE:
$requested_package_line_items = $this
->getRequestedPackageLineItemsAllInOne($shipment);
break;
case static::PACKAGE_INDIVIDUAL:
$requested_package_line_items = $this
->getRequestedPackageLineItemsIndividual($shipment);
break;
case static::PACKAGE_CALCULATE:
$requested_package_line_items = $this
->getRequestedPackageLineItemsCalculate($shipment);
break;
}
return $requested_package_line_items;
}
protected function getPackageVolume(PackageTypeInterface $package_type) {
$unit = $package_type
->getHeight()
->getUnit();
$number = $package_type
->getHeight()
->multiply($package_type
->getWidth()
->convert($unit)
->getNumber())
->multiply($package_type
->getLength()
->convert($unit)
->getNumber())
->getNumber();
return (double) $number;
}
protected function getCleanTitle(ShipmentItem $shipment_item) {
$title = $shipment_item
->getTitle();
$title = preg_replace('/[^A-Za-z0-9 ]/', ' ', $title);
$title = preg_replace('/ +/', ' ', $title);
return trim($title);
}
protected function getRequestedPackageLineItemsAllInOne(ShipmentInterface $shipment) {
$packages = $this
->splitPackages($shipment);
$requested_package_line_items = [];
foreach ($packages as $delta => $package) {
$requested_package_line_item = new RequestedPackageLineItem();
$shipment_title = $shipment
->getTitle();
if (!is_string($shipment_title)) {
$shipment_title = $shipment_title
->render();
}
$requested_package_line_item
->setSequenceNumber($delta + 1)
->setGroupPackageCount(1)
->setWeight(static::packageTotalWeight($package, $shipment
->getPackageType()))
->setDimensions($this
->packageToFedexDimensions($shipment
->getPackageType()))
->setPhysicalPackaging(PhysicalPackagingType::VALUE_BOX)
->setItemDescription($shipment_title);
if ($this->configuration['options']['insurance']) {
$requested_package_line_item
->setInsuredValue(new Money($shipment
->getTotalDeclaredValue()
->getCurrencyCode(), $shipment
->getTotalDeclaredValue()
->getNumber()));
}
$requested_package_line_items[] = $this
->adjustPackage($requested_package_line_item, $package, $shipment);
}
return $requested_package_line_items;
}
protected function getRequestedPackageLineItemsCalculate(ShipmentInterface $shipment) {
$requested_package_line_items = $this
->getRequestedPackageLineItemsAllInOne($shipment);
$packages = $this
->splitPackages($shipment);
foreach ($requested_package_line_items as &$requested_package_line_item) {
$count = static::calculatePackageCount($requested_package_line_item, $packages[$requested_package_line_item
->getSequenceNumber() - 1]);
if ($count) {
$requested_package_line_item
->setGroupPackageCount($count);
$requested_package_line_item
->getWeight()
->setValue($requested_package_line_item
->getWeight()
->getValue() / $count);
}
}
return $requested_package_line_items;
}
protected function getRequestedPackageLineItemsIndividual(ShipmentInterface $shipment) {
$requested_package_line_items = [];
$weightUnits = '';
foreach ($shipment
->getItems() as $delta => $shipment_item) {
$requested_package_line_item = new RequestedPackageLineItem();
if ($weightUnits == '') {
$weightUnits = $shipment_item
->getWeight()
->getUnit();
}
$requested_package_line_item
->setSequenceNumber($delta + 1)
->setGroupPackageCount(1)
->setWeight($this
->physicalWeightToFedex($shipment_item
->getWeight()
->convert($weightUnits)))
->setDimensions($this
->packageToFedexDimensions($shipment
->getPackageType()))
->setPhysicalPackaging(PhysicalPackagingType::VALUE_BOX)
->setItemDescription($this
->getCleanTitle($shipment_item));
if ($this->configuration['options']['insurance']) {
$requested_package_line_item
->setInsuredValue(new Money($shipment_item
->getDeclaredValue()
->getCurrencyCode(), $shipment_item
->getDeclaredValue()
->getNumber()));
}
$this
->adjustPackage($requested_package_line_item, [
$shipment_item,
], $shipment);
$requested_package_line_items[] = $requested_package_line_item;
}
return $requested_package_line_items;
}
protected function getFedExShipment(ShipmentInterface $shipment) {
$line_items = $this
->getRequestedPackageLineItems($shipment);
if ($this->configuration['options']['packaging'] == static::PACKAGE_CALCULATE) {
$count = 0;
foreach ($line_items as $line_item) {
$count += $line_item
->getGroupPackageCount();
}
}
else {
$count = count($line_items);
}
$recipient_address = $shipment
->getShippingProfile()
->get('address')
->first();
$shipper_address = $shipment
->getOrder()
->getStore()
->getAddress();
$fedex_shipment = new RequestedShipment();
$fedex_shipment
->setShipper($this
->getAddressForFedEx($shipper_address))
->setRecipient($this
->getAddressForFedEx($recipient_address))
->setRequestedPackageLineItems($line_items)
->setPackageCount($count)
->setPreferredCurrency($shipment
->getOrder()
->getStore()
->getDefaultCurrencyCode())
->setDropoffType($this->configuration['options']['dropoff'])
->setRateRequestTypes([
$this->configuration['options']['rate_request_type'],
]);
if ($this->configuration['options']['insurance']) {
$fedex_shipment
->setTotalInsuredValue(new Money($shipment
->getTotalDeclaredValue()
->getCurrencyCode(), $shipment
->getTotalDeclaredValue()
->getNumber()));
}
return $fedex_shipment;
}
protected function splitPackages(ShipmentInterface $shipment) {
$packages = [
$shipment
->getItems(),
];
foreach ($this->fedExServiceManager
->getDefinitions() as $plugin_id => $definition) {
$plugin = $this->plugins
->get($plugin_id);
$new_packages = [];
foreach ($packages as $package) {
$new_packages = array_merge($new_packages, $plugin
->splitPackage($package, $shipment));
}
$packages = array_filter($new_packages);
}
return $packages;
}
protected function adjustPackage(RequestedPackageLineItem $requested_package_line_item, array $shipment_items, ShipmentInterface $shipment) {
foreach ($this->fedExServiceManager
->getDefinitions() as $plugin_id => $definition) {
$plugin = $this->plugins
->get($plugin_id);
$requested_package_line_item = $plugin
->adjustPackage($requested_package_line_item, $shipment_items, $shipment);
}
return $requested_package_line_item;
}
protected function getRateRequest(RateService $rate_service, ShipmentInterface $shipment) {
$rate_request = $this->fedExRequest
->getRateRequest($this->configuration);
$rate_request
->setRequestedShipment($this
->getFedExShipment($shipment));
$rate_request
->setVersion($rate_service->version);
$rateRequestEvent = new RateRequestEvent($rate_request, $rate_service, $shipment);
$this->eventDispatcher
->dispatch(CommerceFedExEvents::BEFORE_RATE_REQUEST, $rateRequestEvent);
return $rate_request;
}
protected static function getPackageTotalVolume(array $shipment_items, $volume_unit = NULL) {
if ($volume_unit == NULL) {
$volume_unit = VolumeUnit::CUBIC_CENTIMETER;
}
switch ($volume_unit) {
case VolumeUnit::CUBIC_CENTIMETER:
$linear_unit = PhysicalLengthUnits::CENTIMETER;
break;
case VolumeUnit::CUBIC_INCH:
$linear_unit = PhysicalLengthUnits::INCH;
break;
default:
throw new \Exception("Invalid Units");
}
$order_item_storage = \Drupal::entityTypeManager()
->getStorage('commerce_order_item');
$order_item_ids = [];
foreach ($shipment_items as $shipment_item) {
$order_item_ids[] = $shipment_item
->getOrderItemId();
}
$order_items = $order_item_storage
->loadMultiple($order_item_ids);
$total_volume = 0;
foreach ($order_items as $order_item) {
$purchased_entity = $order_item
->getPurchasedEntity();
if ($purchased_entity
->hasField('dimensions') && !$purchased_entity
->get('dimensions')
->isEmpty()) {
$dimensions = $purchased_entity
->get('dimensions')
->first();
$volume = $dimensions
->getHeight()
->convert($linear_unit)
->multiply($dimensions
->getWidth()
->convert($linear_unit)
->getNumber())
->multiply($dimensions
->getLength()
->convert($linear_unit)
->getNumber())
->getNumber();
$total_volume += (double) $volume * $order_item
->getQuantity();
}
}
return new Volume((string) $total_volume, $volume_unit);
}
protected function getShipmentTotalVolume(ShipmentInterface $shipment) {
$order_item_storage = \Drupal::entityTypeManager()
->getStorage('commerce_order_item');
$order_item_ids = [];
foreach ($shipment
->getItems() as $shipment_item) {
$order_item_ids[] = $shipment_item
->getOrderItemId();
}
$order_items = $order_item_storage
->loadMultiple($order_item_ids);
$unit = $shipment
->getPackageType()
->getHeight()
->getUnit();
$total_volume = 0;
foreach ($order_items as $orderItem) {
$purchased_entity = $orderItem
->getPurchasedEntity();
if ($purchased_entity
->hasField('dimensions') && !$purchased_entity
->get('dimensions')
->isEmpty()) {
$dimensions = $purchased_entity
->get('dimensions')
->first();
$volume = $dimensions
->getHeight()
->convert($unit)
->multiply($dimensions
->getWidth()
->convert($unit)
->getNumber())
->multiply($dimensions
->getLength()
->convert($unit)
->getNumber())
->getNumber();
$total_volume += (double) $volume * $orderItem
->getQuantity();
}
}
return $total_volume;
}
protected function isConfigured() {
$api_information = $this->configuration['api_information'];
return !empty($api_information['api_key']) && !empty($api_information['api_password']) && !empty($api_information['account_number']) && !empty($api_information['meter_number']);
}
public static function physicalWeightToFedex(PhysicalWeight $weight) {
if (!$weight instanceof PhysicalWeight) {
throw new \Exception("Invalid units for weight");
}
$number = $weight
->getNumber();
switch ($weight
->getUnit()) {
case PhysicalWeightUnits::GRAM:
$number /= 1000;
$unit = FedexWeightUnits::VALUE_KG;
break;
case PhysicalWeightUnits::KILOGRAM:
$unit = FedexWeightUnits::VALUE_KG;
break;
case PhysicalWeightUnits::OUNCE:
$number /= 16;
$unit = FedexWeightUnits::VALUE_LB;
break;
case PhysicalWeightUnits::POUND:
$unit = FedexWeightUnits::VALUE_LB;
break;
default:
throw new \Exception("Invalid Units for Weight");
}
return new FedexWeight($unit, $number);
}
public static function packageToFedexDimensions(PackageTypeInterface $package) {
$length = $package
->getLength();
$width = $package
->getWidth();
$height = $package
->getHeight();
if ($width
->getUnit() != $length
->getUnit()) {
$width = $width
->convert($length
->getUnit());
}
if ($height
->getUnit() != $length
->getUnit()) {
$height = $height
->convert($length
->getUnit());
}
switch ($length
->getUnit()) {
case PhysicalLengthUnits::MILLIMETER:
$length = $length
->divide(1000);
$width = $width
->divide(1000);
$height = $height
->divide(1000);
$unit = FedexLengthUnits::VALUE_CM;
break;
case PhysicalLengthUnits::CENTIMETER:
$unit = FedexLengthUnits::VALUE_CM;
break;
case PhysicalLengthUnits::METER:
$length = $length
->multiply(1000);
$width = $width
->multiply(1000);
$height = $height
->multiply(1000);
$unit = FedexLengthUnits::VALUE_CM;
break;
case PhysicalLengthUnits::FOOT:
$length = $length
->multiply(12);
$width = $width
->multiply(12);
$height = $height
->multiply(12);
$unit = FedexLengthUnits::VALUE_IN;
break;
case PhysicalLengthUnits::INCH:
$unit = FedexLengthUnits::VALUE_IN;
break;
default:
throw new \Exception("Invalid Units for Length");
}
return new Dimensions($length
->getNumber(), $width
->getNumber(), $height
->getNumber(), $unit);
}
public static function isDomestic(ShipmentInterface $shipment) {
$shipping_address = $shipment
->getShippingProfile()
->get('address')
->first();
$domestic = $shipping_address instanceof AddressItem ? $shipment
->getOrder()
->getStore()
->getAddress()
->getCountryCode() == $shipping_address
->getCountryCode() : FALSE;
return $domestic;
}
public static function shippingType(ShipmentInterface $shipment) {
return static::isDomestic($shipment) ? FedEx::SHIPPING_TYPE_DOMESTIC : FedEx::SHIPPING_TYPE_INTERNATIONAL;
}
public static function packageTotalWeight(array $shipment_items, PackageTypeInterface $package_type, PhysicalWeight $adjustment = NULL) {
if (empty($shipment_items)) {
return new FedexWeight('LB', 0);
}
$storage = \Drupal::entityTypeManager()
->getStorage('commerce_order_item');
$unit = $storage
->load(reset($shipment_items)
->getOrderItemId())
->getPurchasedEntity()->weight
->first()
->toMeasurement()
->getUnit();
$total_weight = new PhysicalWeight("0", $unit);
foreach ($shipment_items as $shipment_item) {
$order_item = $storage
->load($shipment_item
->getOrderItemId());
$purchased_entity = $order_item
->getPurchasedEntity();
$order_item_weight = $purchased_entity->weight
->first()
->toMeasurement()
->convert($unit)
->multiply($order_item
->getQuantity());
$total_weight = $total_weight
->add($order_item_weight);
}
$total_weight = $total_weight
->add($package_type
->getWeight()
->convert($unit));
if ($adjustment) {
$total_weight = $total_weight
->add($adjustment
->convert($unit));
}
return static::physicalWeightToFedex($total_weight);
}
public static function calculatePackageCount(RequestedPackageLineItem $requested_package_line_item, array $shipment_items) {
$package_volume = static::getFedexPackageVolume($requested_package_line_item
->getDimensions());
$items_volume = static::getPackageTotalVolume($shipment_items, $package_volume
->getUnit());
if ($package_volume
->getNumber() != 0) {
return ceil($items_volume
->getNumber() / $package_volume
->getNumber());
}
return FALSE;
}
public static function getFedexPackageVolume(Dimensions $dimensions) {
$volume_value = $dimensions
->getHeight() * $dimensions
->getLength() * $dimensions
->getWidth();
switch ($dimensions
->getUnits()) {
case LinearUnits::VALUE_CM:
$volume_units = VolumeUnit::CUBIC_CENTIMETER;
break;
case LinearUnits::VALUE_IN:
$volume_units = VolumeUnit::CUBIC_INCH;
break;
default:
throw new \Exception("Invalid Dimensions");
}
return new Volume((string) $volume_value, $volume_units);
}
}