commerce_amws-8.x-1.x-dev/modules/shipping/src/ShipmentService.php
modules/shipping/src/ShipmentService.php
<?php
namespace Drupal\commerce_amws_shipping;
use Drupal\commerce_amws_order\HelperService;
use Drupal\commerce_order\Adjustment;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_price\Price;
use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\commerce_shipping\ShipmentItem;
use Drupal\physical\WeightUnit;
use Drupal\profile\Entity\ProfileInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Psr\Log\LoggerInterface;
/**
* Provides functionality related to importing shipping data from Amazon MWS.
*/
class ShipmentService {
use StringTranslationTrait;
/**
* The shipment type used for new shipments.
*/
const DEFAULT_SHIPMENT_TYPE = 'default';
/**
* The shipment state used for new shipments.
*/
const DEFAULT_SHIPMENT_STATE = 'draft';
/**
* The package type used for new shipments.
*/
const DEFAULT_PACKAGE_TYPE = 'custom_box';
/**
* The profile type to be used for creating the order's shipping profile.
*/
const DEFAULT_SHIPPING_PROFILE_TYPE = 'customer';
/**
* The shipping service to be used for new Amazon MWS orders.
*
* Hardcoded for now, it should be replaced with configuration at first stage,
* and ideally later with mapping between Drupal Commerce shipping methods and
* Amazon MWS shipping services.
*/
const DEFAULT_SHIPPING_SERVICE = 'default';
/**
* The logger.
*
* @var \Psr\Logger\LoggerInterface
*/
protected $logger;
/**
* The helper service for converting address information to profile entities.
*
* @var \Drupal\commerce_amws_order\HelperService
*/
protected $orderHelper;
/**
* The shipment storage.
*
* @var \Drupal\commerce_shipping\ShipmentStorageInterface
*/
protected $shipmentStorage;
/**
* The Amazon MWS shipping configuration.
*
* @var \Drupal\Core\Config\Config
*/
protected $shippingConfig;
/**
* Constructs a new ShipmentService object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\commerce_amws_order\HelperService $order_helper
* The helper service for converting address information to profile
* entities.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration object factory.
* @param \Psr\Logger\LoggerInterface $logger
* The logger channel factory.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
HelperService $order_helper,
ConfigFactoryInterface $config_factory,
LoggerInterface $logger
) {
$this->shipmentStorage = $entity_type_manager->getStorage('commerce_shipment');
$this->orderHelper = $order_helper;
$this->shippingConfig = $config_factory->get('commerce_amws_shipping.settings');
$this->logger = $logger;
}
/**
* Creates a shipment for an Amazon MWS being imported.
*
* The shipping service is defined at the order level by Amazon MWS, even
* though shipping costs are defined at the order item level. We therefore
* always create only one shipment per order. Applications can bundle order
* items in shipments as they wish by adding their own event subscribers.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The Drupal Commerce order.
* @param object $amws_order
* The Amazon MWS order object being improted.
* @param array $amws_order_items
* An array containing the Amazon MWS order item objects.
* @param string $profile_type
* The type of the profile that will hold the shipping address.
*/
public function createShipment(
OrderInterface $order,
\stdClass $amws_order,
array $amws_order_items,
$profile_type
) {
$amws_address = $amws_order->ShippingAddress;
if (!$amws_address) {
throw \InvalidArgumentException(
'Cannot create a shipment for an order that does not have a shipping address.',
);
}
$shipping_profile = $this->orderHelper->amwsAddressToCustomerProfile(
$order,
$amws_address,
$profile_type,
FALSE
);
$phone_field = $this->shippingConfig->get('phone_field_machine_name');
if ($phone_field) {
$shipping_profile->set(
$phone_field,
$amws_address->Phone ?? NULL
);
}
$shipping_profile->save();
$shipment = $this->doCreateShipment(
$order,
$amws_order,
$amws_order_items,
$shipping_profile
);
$order->set('shipments', [$shipment]);
$order->save();
}
/**
* Performs the actual shipment creation for an Amazon MWS order.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The Drupal Commerce order.
* @param object $amws_order
* The Amazon MWS order being improted.
* @param array $amws_order_items
* An array containing the Amazon MWS order item objects.
* @param \Drupal\profile\Entity\ProfileInterface $shipping_profile
* The shipping profile.
*
* @return \Drupal\commerce_shipping\Entity\ShipmentInterface
* The created shipment.
*/
protected function doCreateShipment(
OrderInterface $order,
\stdClass $amws_order,
array $amws_order_items,
ProfileInterface $shipping_profile
) {
$shipment = $this->shipmentStorage->create([
'type' => self::DEFAULT_SHIPMENT_TYPE,
'order_id' => $order->id(),
'package_type' => self::DEFAULT_PACKAGE_TYPE,
'state' => self::DEFAULT_SHIPMENT_STATE,
]);
$shipment->set('title', 'Shipment');
$shipment->setShippingProfile($shipping_profile);
// Shipping method and service.
$shipping_method_id = $this->shippingConfig->get('shipping_method_id');
if ($shipping_method_id) {
$shipment->setShippingMethodId($shipping_method_id);
$shipment->setShippingService(self::DEFAULT_SHIPPING_SERVICE);
}
else {
$message = sprintf(
'No appropriate shipping method was found for Amazon MWS order with ID "%s". The ID of the corresponding Drupal Commerce order is "%s".',
$amws_order->AmazonOrderId,
$order->id()
);
$this->logger->warning($message);
}
$shipment->setAmount($this->shippingAmount($amws_order, $amws_order_items));
$this->doCreateShippingAdjustment($order, $shipment);
// Shipment items.
$shipment_items = [];
foreach ($order->getItems() as $order_item) {
$shipment_items[] = $this->doCreateShipmentItem($order_item);
}
$shipment->setItems($shipment_items);
$shipment->save();
return $shipment;
}
/**
* Creates a shipment item for the given order item.
*
* @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
* The order item for which to create the shipment.
*
* @return \Drupal\commerce_shipping\ShipmentItem
* The created shipment item.
*/
protected function doCreateShipmentItem(OrderItemInterface $order_item) {
$quantity = $order_item->getQuantity();
// Some products may not have weight e.g. gift wraps. We therefore need to
// create shipment items for them as well with zero weight.
$purchased_entity = $order_item->getPurchasedEntity();
if ($purchased_entity->hasField('weight')) {
$weight = $purchased_entity->get('weight')->first()->toMeasurement();
}
else {
$weight = new Weight('0', WeightUnit::POUND);
}
return new ShipmentItem([
'order_item_id' => $order_item->id(),
'title' => $order_item->getTitle(),
'quantity' => $quantity,
'weight' => $weight->multiply($quantity),
'declared_value' => $order_item->getUnitPrice()->multiply($quantity),
]);
}
/**
* Adds a shipping adjustment to the given order and for the given shipment.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order to which to add the adjustment.
* @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
* The shipment for which the adjustment will be created.
*/
protected function doCreateShippingAdjustment(
OrderInterface $order,
ShipmentInterface $shipment
) {
$amount = $shipment->getAmount();
if (!$amount || $amount->getNumber() == '0') {
return;
}
$order->addAdjustment(new Adjustment([
'type' => 'shipping',
'label' => $this->t('Shipping'),
'amount' => $amount,
'source_id' => $shipment->id(),
]));
}
/**
* Calculates the shipping amount for the given Amazon MWS order.
*
* @param object $amws_order
* The Amazon MWS order to calculate the shipping amount for.
* @param array $amws_order_items
* An array containing the Amazon MWS order item objects.
*
* @return \Drupal\commerce_price\Price
* The shipping amount.
*/
protected function shippingAmount(
\stdClass $amws_order,
array $amws_order_items
) {
$amount = new Price('0', $amws_order->OrderTotal->CurrencyCode);
// The shipping amount for the order is the sum of the shipping amount for
// all individual order items.
foreach ($amws_order_items as $amws_order_item) {
if (!empty($amws_order_item->ShippingPrice)) {
$price = new Price(
$amws_order_item->ShippingPrice->Amount,
$amws_order_item->ShippingPrice->CurrencyCode
);
$amount = $amount->add($price);
}
if (!empty($amws_order_item->ShippingDiscount)) {
$discount = new Price(
$amws_order_item->ShippingDiscount->Amount,
$amws_order_item->ShippingDiscount->CurrencyCode
);
$amount = $amount->subtract($discount);
}
}
return $amount;
}
}
