123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Sales\Model\Order;
- use Magento\Framework\Exception\LocalizedException;
- use Magento\Framework\Serialize\Serializer\Json;
- /**
- * Factory class for @see \Magento\Sales\Api\Data\ShipmentInterface
- *
- * @api
- * @since 100.0.2
- */
- class ShipmentFactory
- {
- /**
- * Order converter.
- *
- * @var \Magento\Sales\Model\Convert\Order
- */
- protected $converter;
- /**
- * Shipment track factory.
- *
- * @var \Magento\Sales\Model\Order\Shipment\TrackFactory
- */
- protected $trackFactory;
- /**
- * Instance name to create.
- *
- * @var string
- */
- protected $instanceName;
- /**
- * Serializer
- *
- * @var Json
- */
- private $serializer;
- /**
- * Factory constructor.
- *
- * @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory
- * @param \Magento\Sales\Model\Order\Shipment\TrackFactory $trackFactory
- * @param \Magento\Framework\Serialize\Serializer\Json $serializer
- */
- public function __construct(
- \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory,
- \Magento\Sales\Model\Order\Shipment\TrackFactory $trackFactory,
- Json $serializer = null
- ) {
- $this->converter = $convertOrderFactory->create();
- $this->trackFactory = $trackFactory;
- $this->instanceName = \Magento\Sales\Api\Data\ShipmentInterface::class;
- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
- ->get(Json::class);
- }
- /**
- * Creates shipment instance with specified parameters.
- *
- * @param \Magento\Sales\Model\Order $order
- * @param array $items
- * @param array|null $tracks
- * @return \Magento\Sales\Api\Data\ShipmentInterface
- */
- public function create(\Magento\Sales\Model\Order $order, array $items = [], $tracks = null)
- {
- $shipment = $this->prepareItems($this->converter->toShipment($order), $order, $items);
- if ($tracks) {
- $shipment = $this->prepareTracks($shipment, $tracks);
- }
- return $shipment;
- }
- /**
- * Adds items to the shipment.
- *
- * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
- * @param \Magento\Sales\Model\Order $order
- * @param array $items
- * @return \Magento\Sales\Api\Data\ShipmentInterface
- * @throws LocalizedException
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- */
- protected function prepareItems(
- \Magento\Sales\Api\Data\ShipmentInterface $shipment,
- \Magento\Sales\Model\Order $order,
- array $items = []
- ) {
- $shipmentItems = [];
- foreach ($order->getAllItems() as $orderItem) {
- if ($this->validateItem($orderItem, $items) === false) {
- continue;
- }
- /** @var \Magento\Sales\Model\Order\Shipment\Item $item */
- $item = $this->converter->itemToShipmentItem($orderItem);
- if ($orderItem->getIsVirtual() || ($orderItem->getParentItemId() && !$orderItem->isShipSeparately())) {
- $item->isDeleted(true);
- }
- if ($orderItem->isDummy(true)) {
- $qty = 0;
- if (isset($items[$orderItem->getParentItemId()])) {
- $productOptions = $orderItem->getProductOptions();
- if (isset($productOptions['bundle_selection_attributes'])) {
- $bundleSelectionAttributes = $this->serializer->unserialize(
- $productOptions['bundle_selection_attributes']
- );
- if ($bundleSelectionAttributes) {
- $qty = $bundleSelectionAttributes['qty'] * $items[$orderItem->getParentItemId()];
- $qty = min($qty, $orderItem->getSimpleQtyToShip());
- $item->setQty($this->castQty($orderItem, $qty));
- $shipmentItems[] = $item;
- continue;
- } else {
- $qty = 1;
- }
- }
- } else {
- $qty = 1;
- }
- } else {
- if (isset($items[$orderItem->getId()])) {
- $qty = min($items[$orderItem->getId()], $orderItem->getQtyToShip());
- } elseif (!count($items)) {
- $qty = $orderItem->getQtyToShip();
- } else {
- continue;
- }
- }
- $item->setQty($this->castQty($orderItem, $qty));
- $shipmentItems[] = $item;
- }
- return $this->setItemsToShipment($shipment, $shipmentItems);
- }
- /**
- * Validate order item before shipment
- *
- * @param Item $orderItem
- * @param array $items
- * @return bool
- */
- private function validateItem(\Magento\Sales\Model\Order\Item $orderItem, array $items)
- {
- if (!$this->canShipItem($orderItem, $items)) {
- return false;
- }
- // Remove from shipment items without qty or with qty=0
- if (!$orderItem->isDummy(true)
- && (!isset($items[$orderItem->getId()]) || $items[$orderItem->getId()] <= 0)
- ) {
- return false;
- }
- return true;
- }
- /**
- * Set prepared items to shipment document
- *
- * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
- * @param array $shipmentItems
- * @return \Magento\Sales\Api\Data\ShipmentInterface
- */
- private function setItemsToShipment(\Magento\Sales\Api\Data\ShipmentInterface $shipment, $shipmentItems)
- {
- $totalQty = 0;
- /**
- * Verify that composite products shipped separately has children, if not -> remove from collection
- */
- /** @var \Magento\Sales\Model\Order\Shipment\Item $shipmentItem */
- foreach ($shipmentItems as $key => $shipmentItem) {
- if ($shipmentItem->getOrderItem()->getHasChildren()
- && $shipmentItem->getOrderItem()->isShipSeparately()
- ) {
- $containerId = $shipmentItem->getOrderItem()->getId();
- $childItems = array_filter($shipmentItems, function ($item) use ($containerId) {
- return $containerId == $item->getOrderItem()->getParentItemId();
- });
- if (count($childItems) <= 0) {
- unset($shipmentItems[$key]);
- continue;
- }
- }
- $totalQty += $shipmentItem->getQty();
- $shipment->addItem($shipmentItem);
- }
- return $shipment->setTotalQty($totalQty);
- }
- /**
- * Adds tracks to the shipment.
- *
- * @param \Magento\Sales\Api\Data\ShipmentInterface $shipment
- * @param array $tracks
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return \Magento\Sales\Api\Data\ShipmentInterface
- */
- protected function prepareTracks(\Magento\Sales\Api\Data\ShipmentInterface $shipment, array $tracks)
- {
- foreach ($tracks as $data) {
- if (empty($data['number'])) {
- throw new \Magento\Framework\Exception\LocalizedException(
- __('Please enter a tracking number.')
- );
- }
- $shipment->addTrack(
- $this->trackFactory->create()->addData($data)
- );
- }
- return $shipment;
- }
- /**
- * Checks if order item can be shipped.
- *
- * Dummy item can be shipped or with his children or
- * with parent item which is included to shipment.
- *
- * @param \Magento\Sales\Model\Order\Item $item
- * @param array $items
- * @return bool
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- */
- protected function canShipItem($item, array $items = [])
- {
- if ($item->getIsVirtual() || $item->getLockedDoShip()) {
- return false;
- }
- if ($item->isDummy(true)) {
- if ($item->getHasChildren()) {
- if ($item->isShipSeparately()) {
- return true;
- }
- foreach ($item->getChildrenItems() as $child) {
- if ($child->getIsVirtual()) {
- continue;
- }
- if (empty($items)) {
- if ($child->getQtyToShip() > 0) {
- return true;
- }
- } else {
- if (isset($items[$child->getId()]) && $items[$child->getId()] > 0) {
- return true;
- }
- }
- }
- return false;
- } elseif ($item->getParentItem()) {
- $parent = $item->getParentItem();
- if (empty($items)) {
- return $parent->getQtyToShip() > 0;
- } else {
- return isset($items[$parent->getId()]) && $items[$parent->getId()] > 0;
- }
- }
- } else {
- return $item->getQtyToShip() > 0;
- }
- }
- /**
- * @param Item $item
- * @param string|int|float $qty
- * @return float|int
- */
- private function castQty(\Magento\Sales\Model\Order\Item $item, $qty)
- {
- if ($item->getIsQtyDecimal()) {
- $qty = (double)$qty;
- } else {
- $qty = (int)$qty;
- }
- return $qty > 0 ? $qty : 0;
- }
- }
|