diff --git a/modules/dangerous/config/schema/commerce_fedex_dangerous.yml b/modules/dangerous/config/schema/commerce_fedex_dangerous.yml
new file mode 100644
index 0000000..a709275
--- /dev/null
+++ b/modules/dangerous/config/schema/commerce_fedex_dangerous.yml
@@ -0,0 +1,7 @@
+commerce_fedex.fedex_plugin.plugin.dangerous:
+ type: commerce_fedex_service
+ label: 'Commerce Fedex Dry Ice Service'
+ mapping:
+ contact_number:
+ type: string
+ label: 'Contact Number'
diff --git a/modules/dangerous/src/Plugin/Commerce/EntityTrait/PurchasableEntityDangerousGoods.php b/modules/dangerous/src/Plugin/Commerce/EntityTrait/PurchasableEntityDangerousGoods.php
index 33fee43..363f0d0 100644
--- a/modules/dangerous/src/Plugin/Commerce/EntityTrait/PurchasableEntityDangerousGoods.php
+++ b/modules/dangerous/src/Plugin/Commerce/EntityTrait/PurchasableEntityDangerousGoods.php
@@ -2,7 +2,10 @@
namespace Drupal\commerce_fedex_dangerous\Plugin\Commerce\EntityTrait;
-use Drupal\commerce_fedex_dangerous\PurchasableEntityHazardousBase;
+use Drupal\commerce\BundleFieldDefinition;
+use Drupal\commerce\Plugin\Commerce\EntityTrait\EntityTraitBase;
+use Drupal\commerce_fedex\Plugin\Commerce\ShippingMethod\FedEx;
+use NicholasCreativeMedia\FedExPHP\Enums\DangerousGoodsAccessibilityType;
/**
* Provides the "fedex_dangerous" trait.
@@ -13,14 +16,22 @@
* entity_types = {"commerce_product_variation"}
* )
*/
-class PurchasableEntityDangerousGoods extends PurchasableEntityHazardousBase {
+class PurchasableEntityDangerousGoods extends EntityTraitBase {
/**
* {@inheritdoc}
*/
public function buildFieldDefinitions() {
- $fields = $this->baseFields();
+ $id = $this->getPluginId();
+ $fields[$id . '_accessibility'] = BundleFieldDefinition::create('list_string')
+ ->setLabel($this->t('Require Dangerous Goods/Hazardous Materials Shipping'))
+ ->setCardinality(1)
+ ->setSetting('allowed_values', [0 => "None"] + FedEx::enumToList(DangerousGoodsAccessibilityType::getValidValues()))
+ ->setDisplayOptions('form', [
+ 'type' => 'options_select',
+ 'weight' => 95,
+ ]);
return $fields;
}
diff --git a/modules/dangerous/src/Plugin/Commerce/FedEx/DangerousGoodsPlugin.php b/modules/dangerous/src/Plugin/Commerce/FedEx/DangerousGoodsPlugin.php
new file mode 100644
index 0000000..358c7f8
--- /dev/null
+++ b/modules/dangerous/src/Plugin/Commerce/FedEx/DangerousGoodsPlugin.php
@@ -0,0 +1,114 @@
+ ''] + parent::defaultConfiguration();
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+ $form = parent::buildConfigurationForm($form, $form_state);
+ $form['contact_number'] = [
+ '#type' => 'textfield',
+ '#title' => $this->t("Contact Number"),
+ '#description' => $this->t('Enter the Phone number of your dangerous goods/hazardous materials contact person'),
+ '#default_value' => $this->configuration['contact_number'],
+ ];
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+ parent::submitConfigurationForm($form, $form_state);
+ $this->configuration['contact_number'] = $form_state->getValue('contact_number');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function adjustPackage(RequestedPackageLineItem $package, array $shipment_items, ShipmentInterface $shipment) {
+ $status = $this->getDangerousStatus(reset($shipment_items));
+
+ if ($status === static::NOT_DANGEROUS) {
+ return $package;
+ }
+
+ $special_services_requested = $package->getSpecialServicesRequested();
+ if (empty($special_services_requested)) {
+ $special_services_requested = new PackageSpecialServicesRequested();
+ }
+ $special_services_requested->addToSpecialServiceTypes(PackageSpecialServiceType::VALUE_DANGEROUS_GOODS);
+ $dangerous_goods_detail = $special_services_requested->getDangerousGoodsDetail();
+ if (empty($dangerous_goods_detail)) {
+ $dangerous_goods_detail = new DangerousGoodsDetail();
+ }
+ $dangerous_goods_detail->setAccessibility($status);
+ $special_services_requested->setDangerousGoodsDetail($dangerous_goods_detail);
+ $package->setSpecialServicesRequested($special_services_requested);
+
+ return $package;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function splitPackage(array $shipment_items, ShipmentInterface $shipment) {
+ $packages = [];
+ foreach ($shipment_items as $shipment_item) {
+ $packages[$this->getDangerousStatus($shipment_item)][] = $shipment_item;
+ }
+ return array_values($packages);
+ }
+
+ /**
+ * Returns the DG status of an item.
+ *
+ * @param \Drupal\commerce_shipping\ShipmentItem $shipment_item
+ * The item to check.
+ *
+ * @return mixed
+ * The DG status.
+ */
+ protected function getDangerousStatus(ShipmentItem $shipment_item) {
+ $storage = \Drupal::entityTypeManager()->getStorage('commerce_order_item');
+ /** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
+ $order_item = $storage->load($shipment_item->getOrderItemId());
+ $purchased_entity = $order_item->getPurchasedEntity();
+ if (!$purchased_entity->hasField('fedex_dangerous_accessibility') || $purchased_entity->get('fedex_dangerous_accessibility')->isEmpty()) {
+ return static::NOT_DANGEROUS;
+ }
+ return $purchased_entity->get('fedex_dangerous_accessibility')->value;
+ }
+
+}
diff --git a/modules/dangerous/src/PurchasableEntityHazardousBase.php b/modules/dangerous/src/PurchasableEntityHazardousBase.php
deleted file mode 100644
index d82404e..0000000
--- a/modules/dangerous/src/PurchasableEntityHazardousBase.php
+++ /dev/null
@@ -1,96 +0,0 @@
-getPluginId();
- $label = $this->getLabel();
-
- $fields[$id] = BundleFieldDefinition::create('boolean')
- ->setLabel($label)
- ->setDisplayOptions('form', [
- 'type' => 'boolean_checkbox',
- 'weight' => 95,
- ]);
-
- $fields[$id . '_options'] = BundleFieldDefinition::create('list_string')
- ->setLabel($label . ' ' . $this->t('options'))
- ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
- ->setSetting('allowed_values', [
- HazardousCommodityOptionType::VALUE_BATTERY => $this->t('Battery'),
- HazardousCommodityOptionType::VALUE_HAZARDOUS_MATERIALS => $this->t('Hazardous materials'),
- HazardousCommodityOptionType::VALUE_LIMITED_QUANTITIES_COMMODITIES => $this->t('Limited quantities commodities'),
- HazardousCommodityOptionType::VALUE_ORM_D => $this->t('ORD M'),
- HazardousCommodityOptionType::VALUE_REPORTABLE_QUANTITIES => $this->t('Reportable quantities'),
- HazardousCommodityOptionType::VALUE_SMALL_QUANTITY_EXCEPTION => $this->t('Small quantity exception'),
- ])
- ->setDisplayOptions('form', [
- 'type' => 'options_buttons',
- 'weight' => 95,
- ]);
-
- $fields[$id . '_quantity'] = BundleFieldDefinition::create('float')
- ->setLabel($label . ' ' . $this->t('quantity'))
- ->setDescription($this->t('Specify the quantity of hazardous materials being shipped.'))
- ->setDisplayOptions('form', [
- 'type' => 'number',
- 'weight' => 95,
- ]);
-
- $fields[$id . '_units'] = BundleFieldDefinition::create('list_string')
- ->setLabel($label . ' ' . $this->t('quantity units'))
- ->setDescription($this->t('Specify the unit of measurement to use for the quantity.'))
- ->setSetting('allowed_values', [
- 'ml' => $this->t('ml'),
- 'L' => $this->t('L'),
- 'g' => $this->t('g'),
- 'kg' => $this->t('kg'),
- 'kg G' => $this->t('kg G'),
- ])
- ->setDisplayOptions('form', [
- 'type' => 'options_select',
- 'weight' => 95,
- ]);
-
- $fields[$id . '_regulation_type'] = BundleFieldDefinition::create('list_string')
- ->setLabel($label . ' ' . $this->t('regulation type'))
- ->setDescription($this->t('Select the regulation type of the hazardous materials'))
- ->setSetting('allowed_values', [
- HazardousCommodityRegulationType::VALUE_ADR => $this->t('ADR'),
- HazardousCommodityRegulationType::VALUE_DOT => $this->t('DOT'),
- HazardousCommodityRegulationType::VALUE_IATA => $this->t('IATA'),
- HazardousCommodityRegulationType::VALUE_ORMD => $this->t('ORMD'),
- ])
- ->setDisplayOptions('form', [
- 'type' => 'options_select',
- 'weight' => 95,
- ]);
-
- $fields[$id . '_description'] = BundleFieldDefinition::create('string')
- ->setLabel($label . ' ' . $this->t('description'))
- ->setDescription($this->t('Identify and describe this hazardous commodity.'))
- ->setDisplayOptions('form', [
- 'type' => 'string_textfield',
- 'weight' => 95,
- ]);
-
- return $fields;
- }
-
-}
diff --git a/modules/dry_ice/config/schema/commerce_fedex_dry_ice.yml b/modules/dry_ice/config/schema/commerce_fedex_dry_ice.yml
index 5a0556f..cada769 100644
--- a/modules/dry_ice/config/schema/commerce_fedex_dry_ice.yml
+++ b/modules/dry_ice/config/schema/commerce_fedex_dry_ice.yml
@@ -10,4 +10,15 @@ commerce_fedex.fedex_plugin.plugin.dry_ice:
type: string
label: 'Package Type'
weight:
- type: mapping
+ type: field.value.physical_measurement
+ label: 'Weight'
+ intl:
+ type: mapping
+ label: 'International'
+ mapping:
+ package_type:
+ type: string
+ label: 'Package Type'
+ weight:
+ type: field.value.physical_measurement
+ label: 'Weight'
diff --git a/modules/dry_ice/src/Plugin/Commerce/FedEx/DryIcePlugin.php b/modules/dry_ice/src/Plugin/Commerce/FedEx/DryIcePlugin.php
index a892b88..6f7b71c 100644
--- a/modules/dry_ice/src/Plugin/Commerce/FedEx/DryIcePlugin.php
+++ b/modules/dry_ice/src/Plugin/Commerce/FedEx/DryIcePlugin.php
@@ -2,10 +2,18 @@
namespace Drupal\commerce_fedex_dry_ice\Plugin\Commerce\FedEx;
+use Drupal\address\Plugin\Field\FieldType\AddressItem;
use Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginBase;
+use Drupal\commerce_fedex\Plugin\Commerce\ShippingMethod\FedEx;
+use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\commerce_shipping\PackageTypeManagerInterface;
+use Drupal\commerce_shipping\ShipmentItem;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\physical\Weight;
use Drupal\physical\WeightUnit;
+use NicholasCreativeMedia\FedExPHP\Enums\PackageSpecialServiceType;
+use NicholasCreativeMedia\FedExPHP\Structs\PackageSpecialServicesRequested;
+use NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -14,12 +22,15 @@
* @CommerceFedExPlugin(
* id = "dry_ice",
* label = @Translation("FedEx Dry Ice"),
- * options_label = @Translation("Dry Ice Shipment Options")
- * options_description = @Translation("
+ * options_label = @Translation("Dry Ice Shipment Options"),
+ * options_description = @Translation("Enter your global shipping options for dry ice shipments")
* )
*/
class DryIcePlugin extends FedExPluginBase {
+ const NOT_DRY_ICE = 0;
+ const DRY_ICE = 1;
+
/**
* The Package Type Manager.
*
@@ -144,4 +155,128 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function adjustPackage(RequestedPackageLineItem $package, array $shipment_items, ShipmentInterface $shipment) {
+ $type = $this->getType($shipment);
+ if (!$this->verifyPackage($shipment_items, $type)) {
+ throw new \Exception("Package cannot be shipped, mix of Dry Ice and non Dry Ice Items");
+ }
+
+ if ($this->isDryIceItem(reset($shipment_items), $type)) {
+ $special_services_requested = $package->getSpecialServicesRequested();
+ if (empty($special_services_requested)) {
+ $special_services_requested = new PackageSpecialServicesRequested();
+ }
+ $dry_ice_weight = new Weight($this->configuration[$type]['weight']['number'], $this->configuration[$type]['weight']['unit']);
+ $special_services_requested->addToSpecialServiceTypes(PackageSpecialServiceType::VALUE_DRY_ICE);
+ $special_services_requested->setDryIceWeight(FedEx::physicalWeightToFedex($dry_ice_weight));
+ /** @var \Drupal\commerce_shipping\Plugin\Commerce\PackageType\PackageType $package_type */
+ $package_type = $this->packageTypeManager->createInstance($this->configuration[$type]['package_type']);
+ $package->setDimensions(Fedex::packageToFedexDimensions($package_type));
+ $package->setWeight(FedEx::packageTotalWeight($shipment_items, $package_type, $dry_ice_weight));
+ $package->setSpecialServicesRequested($special_services_requested);
+ }
+ $package = parent::adjustPackage($package, $shipment_items, $shipment);
+ return $package;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function splitPackage(array $shipment_items, ShipmentInterface $shipment) {
+ $type = $this->getType($shipment);
+ $packages = [static::NOT_DRY_ICE => [], static::DRY_ICE => []];
+ foreach ($shipment_items as $shipment_item) {
+ if ($this->isDryIceItem($shipment_item, $type)) {
+ $packages[static::DRY_ICE][] = $shipment_item;
+ }
+ else {
+ $packages[static::NOT_DRY_ICE][] = $shipment_item;
+ }
+ }
+
+ return $packages;
+ }
+
+ /**
+ * Verified a package has all dry ic eor non-dry ice items.
+ *
+ * @param array $shipment_items
+ * The shipment items to check.
+ * @param string $type
+ * The type, 'domestic' or 'intl' depending on the shipping distance.
+ *
+ * @return bool
+ * True if the package is internally consistant.
+ */
+ protected function verifyPackage(array $shipment_items, string $type) {
+ $dryIceBox = $this->isDryIceBox($shipment_items, $type);
+ $storage = \Drupal::entityTypeManager()->getStorage('commerce_order_item');
+ foreach ($shipment_items as $shipment_item) {
+ if ($dryIceBox xor $this->isDryIceItem($shipment_item, $type)) {
+ return FALSE;
+ }
+
+ }
+ return TRUE;
+ }
+
+ /**
+ * Determine whether a droup of shipment items requires dry ice shipping.
+ *
+ * @param array $shipment_items
+ * The shipment items to check.
+ * @param string $type
+ * The type, 'domestic' or 'intl' depending on the shipping distance.
+ *
+ * @return bool
+ * True if the package needs dry ice shipping.
+ */
+ protected function isDryIceBox(array $shipment_items, string $type) {
+ return $this->isDryIceItem(reset($shipment_items), $type);
+ }
+
+ /**
+ * Determine whether a shipment item requires dry ice shipping or not.
+ *
+ * @param \Drupal\commerce_shipping\ShipmentItem $shipment_item
+ * The shipment item.
+ * @param string $type
+ * The type, 'domestic' or 'intl' depending on the shipping distance.
+ *
+ * @return bool
+ * true if the shipment item requires dry ice shipping.
+ */
+ protected function isDryIceItem(ShipmentItem $shipment_item, string $type) {
+ $storage = \Drupal::entityTypeManager()->getStorage('commerce_order_item');
+ /** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
+ $order_item = $storage->load($shipment_item->getOrderItemId());
+ $purchased_entity = $order_item->getPurchasedEntity();
+ return $purchased_entity->hasField('fedex_dry_ice_' . $type) && !$purchased_entity->get('fedex_dry_ice_' . $type)->isEmpty() && $purchased_entity->get('fedex_dry_ice_' . $type)->first()->getValue()['value'] == 1;
+
+ }
+
+ /**
+ * Determine if this is a domestic or international shipment.
+ *
+ * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
+ * The shipment to check.
+ *
+ * @return string
+ * either 'domestic' or 'intl'
+ */
+ protected function getType(ShipmentInterface $shipment) {
+
+ /* @var AddressItem $shipping_address */
+ $shipping_address = $shipment->getShippingProfile()->get('address')->first();
+
+ $domestic = ($shipping_address instanceof AddressItem)
+ ? $shipment->getOrder()->getStore()->getAddress()->getCountryCode() == $shipping_address->getCountryCode()
+ : FALSE;
+
+ return $domestic ? 'domestic' : 'intl';
+ }
+
}
diff --git a/src/Annotation/CommerceFedExPlugin.php b/src/Annotation/CommerceFedExPlugin.php
index bdcdd45..f62c7f2 100644
--- a/src/Annotation/CommerceFedExPlugin.php
+++ b/src/Annotation/CommerceFedExPlugin.php
@@ -48,4 +48,5 @@ class CommerceFedExPlugin extends Plugin {
* @ingroup plugin_translatable
*/
public $options_description;
+
}
diff --git a/src/Plugin/Commerce/FedEx/FedExPluginBase.php b/src/Plugin/Commerce/FedEx/FedExPluginBase.php
index 05bfcf4..907c7e8 100644
--- a/src/Plugin/Commerce/FedEx/FedExPluginBase.php
+++ b/src/Plugin/Commerce/FedEx/FedExPluginBase.php
@@ -2,11 +2,13 @@
namespace Drupal\commerce_fedex\Plugin\Commerce\FedEx;
+use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
+use NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -96,4 +98,28 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
return $form;
}
+ /**
+ * Adjust a package based on the items, shipment and profile.
+ *
+ * @param \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem $package
+ * The package to adjust.
+ * @param array $items
+ * An array of shipment items.
+ * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
+ * The shipment.
+ *
+ * @return \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem
+ * The adjusted Package.
+ */
+ public function adjustPackage(RequestedPackageLineItem $package, array $items, ShipmentInterface $shipment) {
+ return $package;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function splitPackage(array $shipment_items, ShipmentInterface $shipment) {
+ return [$shipment_items];
+ }
+
}
diff --git a/src/Plugin/Commerce/FedEx/FedExPluginInterface.php b/src/Plugin/Commerce/FedEx/FedExPluginInterface.php
index 75098d0..43df6f3 100644
--- a/src/Plugin/Commerce/FedEx/FedExPluginInterface.php
+++ b/src/Plugin/Commerce/FedEx/FedExPluginInterface.php
@@ -2,9 +2,11 @@
namespace Drupal\commerce_fedex\Plugin\Commerce\FedEx;
+use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Plugin\PluginFormInterface;
+use NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem;
/**
* Defines the base interface for FedEx Service Plugins.
@@ -13,4 +15,32 @@
*/
interface FedExPluginInterface extends ConfigurablePluginInterface, PluginFormInterface, PluginInspectionInterface {
+ /**
+ * Function adjustPackage.
+ *
+ * @param \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem $package
+ * The package to adjust.
+ * @param array $items
+ * An array of Shipment Items.
+ * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
+ * The Shipment.
+ *
+ * @return \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem
+ * The Adjusted Package
+ */
+ public function adjustPackage(RequestedPackageLineItem $package, array $items, ShipmentInterface $shipment);
+
+ /**
+ * Function splitPackage.
+ *
+ * @param array $shipment_items
+ * An Array of shipment items.
+ * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
+ * The Shipment.
+ *
+ * @return array
+ * An array of arrays of shipment items.
+ */
+ public function splitPackage(array $shipment_items, ShipmentInterface $shipment);
+
}
diff --git a/src/Plugin/Commerce/ShippingMethod/FedEx.php b/src/Plugin/Commerce/ShippingMethod/FedEx.php
index 6885d95..0615ac9 100644
--- a/src/Plugin/Commerce/ShippingMethod/FedEx.php
+++ b/src/Plugin/Commerce/ShippingMethod/FedEx.php
@@ -18,18 +18,20 @@
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 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\PackageSpecialServicesRequested;
use NicholasCreativeMedia\FedExPHP\Structs\Party;
use NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem;
use NicholasCreativeMedia\FedExPHP\Structs\RequestedShipment;
@@ -217,10 +219,7 @@ public function defaultConfiguration() {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
/* @todo Remove hack when commerce issue #2859423 is resolved */
- if ($this->configuration == $this->defaultConfiguration()) {
- $this->fixConfiguration($form_state);
- }
-
+ // $this->fixConfiguration($form_state);
$form = parent::buildConfigurationForm($form, $form_state);
// Select all services by default.
@@ -243,10 +242,10 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
'#default_value' => $this->configuration['api_information']['api_key'],
];
$form['api_information']['api_password'] = [
- '#type' => 'textfield',
+ '#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' => $this->configuration['api_information']['api_password'],
+ '#default_value' => '',
];
$form['api_information']['account_number'] = [
'#type' => 'number',
@@ -349,9 +348,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
$plugin = $this->plugins->get($plugin_id);
$subform = [
'#type' => 'details',
- '#title' => $definition['optionsLabel']->render(),
+ '#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;
}
@@ -361,14 +364,15 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
/* @todo Remove hack when commerce issue #2859423 is resolved */
- $this->fixConfiguration($form_state);
-
+ // $this->fixConfiguration($form_state);
parent::validateConfigurationForm($form, $form_state);
foreach ($this->fedExServiceManager->getDefinitions() as $plugin_id => $definition) {
- /** @var \Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginInterface $plugin */
- $plugin = $this->plugins->get($plugin_id);
- $plugin->validateConfigurationForm($form[$plugin_id], SubformState::createForSubform($form[$plugin_id], $form_state->getCompleteForm(), $form_state));
+ if (!empty($form[$plugin_id])) {
+ /** @var \Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginInterface $plugin */
+ $plugin = $this->plugins->get($plugin_id);
+ $plugin->validateConfigurationForm($form[$plugin_id], SubformState::createForSubform($form[$plugin_id], $form_state->getCompleteForm(), $form_state));
+ }
}
}
@@ -377,8 +381,7 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
/* @todo Remove hack when commerce issue #2859423 is resolved */
- $this->fixConfiguration($form_state);
-
+ // $this->fixConfiguration($form_state);
if (!$form_state->getErrors()) {
$values = $form_state->getValue($form['#parents']);
@@ -402,8 +405,11 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
foreach ($this->fedExServiceManager->getDefinitions() as $plugin_id => $definition) {
/** @var \Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginInterface $plugin */
$plugin = $this->plugins->get($plugin_id);
- $plugin->submitConfigurationForm($form[$plugin_id], SubformState::createForSubform($form[$plugin_id], $form_state->getCompleteForm(), $form_state));
+ 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);
@@ -422,8 +428,13 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
* Whether to skip the check to log or not.
*/
protected function logRequest($message, $data, $level = LogLevel::INFO, $skip_config_check = FALSE) {
- if ($skip_config_check || $this->configuration['options']['log']['request']) {
- $this->watchdog->log($level, "$message
@rate_request", [
+ /* If we are using the old configuration */
+ if (is_array($this->configuration['options']['log'])) {
+
+ }
+
+ if ($skip_config_check || @$this->configuration['options']['log']['request']) {
+ $this->watchdog->log($level, "$message
@rate_request", [ '@rate_request' => var_export($data, TRUE), ]); } @@ -481,7 +492,7 @@ public function calculateRates(ShipmentInterface $shipment) { $price = new Price((string) $cost->getAmount(), $cost->getCurrency()); if ($multiplier != 1) { - $price = $price->multiply((string)$multiplier); + $price = $price->multiply((string) $multiplier); } $price = $this->rounder->round($price, $round); @@ -543,9 +554,9 @@ public function getPlugins() { * @return array * The options list. */ - protected static function enumToList(array $enums) { + public static function enumToList(array $enums) { return array_combine($enums, array_map(function ($d) { - return ucwords(str_replace('_', ' ', $d)); + return ucwords(strtolower(str_replace('_', ' ', $d))); }, $enums)); } @@ -652,30 +663,38 @@ protected function getCleanTitle(ShipmentItem $shipment_item) { * The package line items. */ protected function getRequestedPackageLineItemsAllInOne(ShipmentInterface $shipment) { - $requested_package_line_item = new RequestedPackageLineItem(); - $shipment_title = $shipment->getTitle(); - if (!is_string($shipment_title)) { - $shipment_title = $shipment_title->render(); - } + $packages = $this->splitPackages($shipment); - $requested_package_line_item - ->setSequenceNumber(1) - ->setGroupPackageCount(1) - ->setWeight($this->physicalWeightToFedex($shipment->getWeight())) - ->setDimensions($this->packageToFedexDimensions($shipment->getPackageType())) - ->setPhysicalPackaging(PhysicalPackagingType::VALUE_BOX) - ->setItemDescription($shipment_title) - ->setSpecialServicesRequested(new PackageSpecialServicesRequested()); + $requested_package_line_items = []; - if ($this->configuration['options']['insurance']) { - $requested_package_line_item->setInsuredValue(new Money( - $shipment->getTotalDeclaredValue()->getCurrencyCode(), - $shipment->getTotalDeclaredValue()->getNumber() - )); + 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_item]; + return $requested_package_line_items; } /** @@ -688,39 +707,18 @@ protected function getRequestedPackageLineItemsAllInOne(ShipmentInterface $shipm * The package line items. */ protected function getRequestedPackageLineItemsCalculate(ShipmentInterface $shipment) { - $package_volume = $this->getPackageVolume($shipment->getPackageType()); - $total_volume = $this->getShipmentTotalVolume($shipment); - - $count = ($total_volume == 0 || $total_volume < $package_volume) - ? 1 - : ceil($total_volume / $package_volume); - - $package_weight = $shipment->getWeight()->divide((string) $count); - - $shipment_title = $shipment->getTitle(); - if (!is_string($shipment_title)) { - $shipment_title = $shipment_title->render(); - } - - $requested_package_line_item = new RequestedPackageLineItem(); - - $requested_package_line_item - ->setGroupNumber(1) - ->setGroupPackageCount($count) - ->setWeight($this->physicalWeightToFedex($package_weight)) - ->setDimensions($this->packageToFedexDimensions($shipment->getPackageType())) - ->setPhysicalPackaging(PhysicalPackagingType::VALUE_BOX) - ->setItemDescription($shipment_title) - ->setSpecialServicesRequested(new PackageSpecialServicesRequested()); - - if ($this->configuration['options']['insurance']) { - $requested_package_line_item->setInsuredValue(new Money( - $shipment->getTotalDeclaredValue()->getCurrencyCode(), - $shipment->getTotalDeclaredValue()->getNumber() - )); + $requested_package_line_items = $this->getRequestedPackageLineItemsAllInOne($shipment); + $packages = $this->splitPackages($shipment); + foreach ($requested_package_line_items as &$requested_package_line_item) { + /** @var \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem $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_item]; + return $requested_package_line_items; } /** @@ -734,18 +732,27 @@ protected function getRequestedPackageLineItemsCalculate(ShipmentInterface $ship */ protected function getRequestedPackageLineItemsIndividual(ShipmentInterface $shipment) { $requested_package_line_items = []; - + $weightUnits = ''; foreach ($shipment->getItems() as $delta => $shipment_item) { + $qty = $shipment_item->getQuantity(); $requested_package_line_item = new RequestedPackageLineItem(); + if ($weightUnits == '') { + /* All packages must have the same Weight Unit */ + $weightUnits = $shipment_item->getWeight()->getUnit(); + } + + $weight = $shipment_item + ->getWeight() + ->divide($qty) + ->convert($weightUnits); $requested_package_line_item ->setSequenceNumber($delta + 1) - ->setGroupPackageCount(1) - ->setWeight($this->physicalWeightToFedex($shipment_item->getWeight())) + ->setGroupPackageCount($qty) + ->setWeight($this->physicalWeightToFedex($weight)) ->setDimensions($this->packageToFedexDimensions($shipment->getPackageType())) ->setPhysicalPackaging(PhysicalPackagingType::VALUE_BOX) - ->setItemDescription($this->getCleanTitle($shipment_item)) - ->setSpecialServicesRequested(new PackageSpecialServicesRequested()); + ->setItemDescription($this->getCleanTitle($shipment_item)); if ($this->configuration['options']['insurance']) { $requested_package_line_item->setInsuredValue(new Money( @@ -753,7 +760,7 @@ protected function getRequestedPackageLineItemsIndividual(ShipmentInterface $shi $shipment_item->getDeclaredValue()->getNumber() )); } - + $this->adjustPackage($requested_package_line_item, [$shipment_item], $shipment); $requested_package_line_items[] = $requested_package_line_item; } @@ -772,9 +779,10 @@ protected function getRequestedPackageLineItemsIndividual(ShipmentInterface $shi protected function getFedExShipment(ShipmentInterface $shipment) { $line_items = $this->getRequestedPackageLineItems($shipment); - $count = ($this->configuration['options']['packaging'] == static::PACKAGE_CALCULATE) - ? $line_items[0]->getGroupPackageCount() - : count($line_items); + $count = 0; + foreach ($line_items as $line_item) { + $count += $line_item->getGroupPackageCount(); + } /** @var \Drupal\address\AddressInterface $recipient_address */ $recipient_address = $shipment->getShippingProfile()->get('address')->first(); @@ -783,7 +791,6 @@ protected function getFedExShipment(ShipmentInterface $shipment) { $fedex_shipment = new RequestedShipment(); $fedex_shipment - ->setTotalWeight($this->physicalWeightToFedex($shipment->getWeight())) ->setShipper($this->getAddressForFedEx($shipper_address)) ->setRecipient($this->getAddressForFedEx($recipient_address)) ->setRequestedPackageLineItems($line_items) @@ -803,6 +810,51 @@ protected function getFedExShipment(ShipmentInterface $shipment) { return $fedex_shipment; } + /** + * Queries plugins to split shipments into fedex packages. + * + * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment + * The shipment to split. + * + * @return array + * An array of arrays or shipment items. + */ + protected function splitPackages(ShipmentInterface $shipment) { + $packages = [$shipment->getItems()]; + foreach ($this->fedExServiceManager->getDefinitions() as $plugin_id => $definition) { + /** @var \Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginInterface $plugin */ + $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; + } + + /** + * Query plugins for package adjustments. + * + * @param \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem $requested_package_line_item + * Te package line item to be adjusted. + * @param array $shipment_items + * The shipment items in the package. + * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment + * The full shipment. + * + * @return \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem + * The adjusted line item. + */ + protected function adjustPackage(RequestedPackageLineItem $requested_package_line_item, array $shipment_items, ShipmentInterface $shipment) { + foreach ($this->fedExServiceManager->getDefinitions() as $plugin_id => $definition) { + /** @var \Drupal\commerce_fedex\Plugin\Commerce\FedEx\FedExPluginInterface $plugin */ + $plugin = $this->plugins->get($plugin_id); + $requested_package_line_item = $plugin->adjustPackage($requested_package_line_item, $shipment_items, $shipment); + } + return $requested_package_line_item; + } + /** * Gets a rate request object for FedEx. * @@ -826,6 +878,64 @@ protected function getRateRequest(RateService $rate_service, ShipmentInterface $ return $rate_request; } + /** + * Return the total volume of an array of shipment items in a given unit. + * + * @param array $shipment_items + * The array of shipment items. + * @param string $volume_unit + * The volume unit. + * + * @return \Drupal\physical\Volume + * The total Volume. + * + * @throws \Exception + */ + protected static function getPackageTotalVolume(array $shipment_items, string $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) { + /** @var \Drupal\commerce_order\Entity\OrderItem $order_item */ + /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $purchased_entity */ + $purchased_entity = $order_item->getPurchasedEntity(); + + if ($purchased_entity->hasField('dimensions') && !$purchased_entity->get('dimensions')->isEmpty()) { + /** @var \Drupal\physical\Plugin\Field\FieldType\DimensionsItem $dimensions */ + $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 += (float) $volume * $order_item->getQuantity(); + } + } + + return new Volume((string) $total_volume, $volume_unit); + } + /** * Gets the shipment's total volume in the same units as the package type. * @@ -897,7 +1007,7 @@ protected function isConfigured() { * * @throws \Exception */ - protected function physicalWeightToFedex(PhysicalWeight $weight) { + public static function physicalWeightToFedex(PhysicalWeight $weight) { if (!$weight instanceof PhysicalWeight) { throw new \Exception("Invalid units for weight"); } @@ -941,7 +1051,7 @@ protected function physicalWeightToFedex(PhysicalWeight $weight) { * * @throws \Exception */ - protected function packageToFedexDimensions(PackageTypeInterface $package) { + public static function packageToFedexDimensions(PackageTypeInterface $package) { $length = $package->getLength(); $width = $package->getWidth(); $height = $package->getHeight(); @@ -1044,4 +1154,93 @@ private function fixConfiguration(FormStateInterface $form_state) { } } + /** + * Function packageTotalWeight. + * + * @param array $shipment_items + * An array of shipment items. + * @param \Drupal\commerce_shipping\Plugin\Commerce\PackageType\PackageTypeInterface $package_type + * The commerce_shipping package type. + * @param \Drupal\physical\Weight|null $adjustment + * Any weight adjustment to add or subtract. + * + * @return \NicholasCreativeMedia\FedExPHP\Structs\Weight + * The total weight of the package. + */ + public static function packageTotalWeight(array $shipment_items, PackageTypeInterface $package_type, $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) { + /* @var ShipmentItem $shipment_item */ + /* @var \Drupal\commerce_order\Entity\OrderItem $order_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); + } + + /** + * Determines a package count given package and item volumes. + * + * @param \NicholasCreativeMedia\FedExPHP\Structs\RequestedPackageLineItem $requested_package_line_item + * The package line item containing the fedex package dimensions. + * @param array $shipment_items + * The array of shipment items. + * + * @return bool|float + * The number of needed packages, or False on error. + */ + 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; + + } + + /** + * Determine the package volume from a fedex package dimensions item. + * + * @param \NicholasCreativeMedia\FedExPHP\Structs\Dimensions $dimensions + * The fexed package dimensions item. + * + * @return \Drupal\physical\Volume + * The total package volume. + * + * @throws \Exception + */ + 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); + } + } diff --git a/tests/src/Unit/FedExRequestTest.php b/tests/src/Unit/FedExRequestTest.php new file mode 100644 index 0000000..82422ac --- /dev/null +++ b/tests/src/Unit/FedExRequestTest.php @@ -0,0 +1,89 @@ +request = new FedExRequest(); + $this->configuration = [ + 'api_information' => [ + 'api_key' => 'testkey', + 'api_password' => 'testpass', + 'account_number' => '1234567', + 'meter_number' => '9876543', + 'mode' => 'test', + ], + ]; + } + + /** + * @covers ::getRateRequest + */ + public function testRateRequest() { + + $rate_request = $this->request->getRateRequest($this->configuration); + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Structs\RateRequest", $rate_request); + + $client_detail = $rate_request->getClientDetail(); + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Structs\ClientDetail", $client_detail); + $this->assertEquals('1234567', $client_detail->getAccountNumber()); + $this->assertEquals('9876543', $client_detail->getMeterNumber()); + + $web_authentication_detail = $rate_request->getWebAuthenticationDetail(); + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Structs\WebAuthenticationDetail", $web_authentication_detail); + + $user_credential = $web_authentication_detail->getUserCredential(); + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Structs\WebAuthenticationCredential", $user_credential); + + $this->assertEquals('testkey', $user_credential->getKey()); + $this->assertEquals('testpass', $user_credential->getPassword()); + } + + /** + * @covers ::getRateService + */ + public function testRateService() { + $rate_service = $this->request->getRateService($this->configuration); + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Services\RateService", $rate_service); + + /** @var \NicholasCreativeMedia\FedExPHP\Structs\VersionId $version */ + $version = $rate_service->version; + $this->assertInstanceOf("\NicholasCreativeMedia\FedExPHP\Structs\VersionId", $version); + + $this->assertEquals(static::RATE_SERVICE_SERVICE_ID, $version->getServiceId()); + $this->assertEquals(static::RATE_SERVICE_MAJOR_VERSION, $version->getMajor()); + } + +}