Your IP : 127.0.0.1


Current Path : /home/dev2.destoffenstraat.com/app/Firebear/ImportExport/Model/Import/Order/Shipment/
Upload File :
Current File : /home/dev2.destoffenstraat.com/app/Firebear/ImportExport/Model/Import/Order/Shipment/Track.php

<?php
/**
 * @copyright: Copyright © 2020 Firebear Studio. All rights reserved.
 * @author   : Firebear Studio <fbeardev@gmail.com>
 */
namespace Firebear\ImportExport\Model\Import\Order\Shipment;

use Firebear\ImportExport\Model\ResourceModel\Order\Helper;
use Magento\Framework\DB\TransactionFactory;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\ShipmentFactory;
use Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory as ShipmentCollectionFactory;
use Magento\Shipping\Model\ShipmentNotifierFactory;
use Firebear\ImportExport\Model\Import\Order\AbstractAdapter;
use Firebear\ImportExport\Model\Import\Context;

/**
 * Order Track Import
 */
class Track extends AbstractAdapter
{
    /**
     * Entity Type Code
     */
    const ENTITY_TYPE_CODE = 'order';

    /**
     * Prefix of Fields
     *
     */
    const PREFIX = 'shipment_track';

    /**
     * Entity Id Column Name
     */
    const COLUMN_ENTITY_ID = 'entity_id';

    /**
     * Shipment Id Column Name
     */
    const COLUMN_SHIPMENT_ID = 'parent_id';

    /**
     * Shipment Increment Id Column Name
     */
    const COLUMN_SHIPMENT_INCREMENT_ID = 'shipment_increment_id';

    /**
     * Order Id Column Name
     */
    const COLUMN_ORDER_ID = 'order_id';

    /**
     * Track Number Column Name
     */
    const COLUMN_TRACK_NUMBER = 'track_number';

    /**
     * Error Codes
     */
    const ERROR_ENTITY_ID_IS_EMPTY = 'shipmentTrackIdIsEmpty';
    const ERROR_SHIPMENT_ID_IS_EMPTY = 'shipmentTrackParentIdIsEmpty';
    const ERROR_DUPLICATE_ENTITY_ID = 'duplicateShipmentTrackId';
    const ERROR_ORDER_ID_IS_EMPTY = 'shipmentTrackOrderIdIsEmpty';
    const ERROR_TRACK_NUMBER_IS_EMPTY = 'shipmentTrackNumberIsEmpty';
    const ERROR_SHIPMENT_INCREMENT_ID = 'shipmentTrackIncrementId';
    const ERROR_ORDER_INCREMENT_ID = 'orderTrackIncrementId';
    const ERROR_SHIPMENT_COUNT = 'shipmentItemCount';
    const ERROR_SHIPMENT_IS_EMPTY = 'shipmentItemIsEmpty';
    const ERROR_SKUS_INCORRECT = 'skusIncorrect';
    const ERROR_QTY_INCORRECT = 'qtyIncorrect';
    const ERROR_SKU_NOT_FOUND = 'skuNotFound';

    /**
     * Validation Failure Message Template Definitions
     *
     * @var array
     */
    protected $_messageTemplates = [
        self::ERROR_DUPLICATE_ENTITY_ID => 'Shipment Track entity_id is found more than once in the import file',
        self::ERROR_ENTITY_ID_IS_EMPTY => 'Shipment Track entity_id is empty',
        self::ERROR_SHIPMENT_ID_IS_EMPTY => 'Shipment Track parent_id is empty',
        self::ERROR_ORDER_ID_IS_EMPTY => 'Shipment Track order_id is empty',
        self::ERROR_TRACK_NUMBER_IS_EMPTY => 'Shipment Track track_number is empty',
        self::ERROR_SHIPMENT_INCREMENT_ID => 'Shipment with selected shipment:increment_id does not exist',
        self::ERROR_ORDER_INCREMENT_ID => 'Order with selected increment_id does not exist',
        self::ERROR_SHIPMENT_COUNT => 'Order has more than 1 Shipment. please specify Shipment ID.',
        self::ERROR_SHIPMENT_IS_EMPTY => 'Order does not have Shipment.',
        self::ERROR_SKUS_INCORRECT => 'Skus string is incorrect.',
        self::ERROR_QTY_INCORRECT => 'Product with sku %s has incorrect qty.',
        self::ERROR_SKU_NOT_FOUND => 'Product with sku %s does not exist.',
    ];

    /**
     * Order Shipment Track Table Name
     *
     * @var string
     */
    protected $_mainTable = 'sales_shipment_track';

    /**
     * Shipment Collection
     *
     * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Collection;
     */
    protected $shipmentCollection;

    /**
     * Shipment Collection Factory
     *
     * @var \Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory;
     */
    protected $shipmentCollectionFactory;

    /**
     * Shipment Notifier
     *
     * @var \Magento\Shipping\Model\ShipmentNotifier;
     */
    protected $notifier;

    /**
     * Shipment Notifier Factory
     *
     * @var \Magento\Shipping\Model\ShipmentNotifierFactory;
     */
    protected $notifierFactory;

    /**
     * Shipment Factory
     *
     * @var ShipmentFactory
     */
    protected $shipmentFactory;

    /**
     * Order Repository
     *
     * @var OrderRepositoryInterface
     */
    protected $orderRepository;

    /**
     * Transaction Factory
     *
     * @var TransactionFactory
     */
    protected $transactionFactory;

    /**
     * Track constructor
     *
     * @param Context $context
     * @param Helper $resourceHelper
     * @param ShipmentCollectionFactory $shipmentCollectionFactory
     * @param ShipmentNotifierFactory $notifierFactory
     * @param ShipmentFactory $shipmentFactory
     * @param OrderRepositoryInterface $orderRepository
     * @param TransactionFactory $transactionFactory
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function __construct(
        Context $context,
        Helper $resourceHelper,
        ShipmentCollectionFactory $shipmentCollectionFactory,
        ShipmentNotifierFactory $notifierFactory,
        ShipmentFactory $shipmentFactory,
        OrderRepositoryInterface $orderRepository,
        TransactionFactory $transactionFactory
    ) {
        $this->shipmentCollectionFactory = $shipmentCollectionFactory;
        $this->notifierFactory = $notifierFactory;
        $this->shipmentFactory = $shipmentFactory;
        $this->orderRepository = $orderRepository;
        $this->transactionFactory = $transactionFactory;

        parent::__construct(
            $context,
            $resourceHelper
        );
    }

    /**
     * Retrieve The Prepared Data
     *
     * @param array $rowData
     * @return array|bool
     */
    public function prepareRowData(array $rowData)
    {
        $this->prepareCurrentOrderId($rowData);
        $rowData = $this->_extractField($rowData, static::PREFIX);
        return (count($rowData) && !$this->isEmptyRow($rowData))
            ? $rowData
            : false;
    }

    /**
     * Retrieve Entity Id If Entity Is Present In Database
     *
     * @param array $rowData
     * @return bool|int
     */
    protected function _getExistEntityId(array $rowData)
    {
        $bind = [
            ':order_id' => $this->_getOrderId($rowData),
            ':parent_id' => $this->_getShipmentId($rowData),
            ':track_number' => $rowData['track_number']
        ];
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from($this->getMainTable(), 'entity_id')
            ->where('parent_id = :parent_id')
            ->where('order_id = :order_id')
            ->where('track_number = :track_number');

        return $this->_connection->fetchOne($select, $bind);
    }

    /**
     * Retrieve item skus
     *
     * @param string $skus
     * @return array
     */
    protected function getSkus($skus)
    {
        $data = [];
        foreach (explode(';', trim($skus, ';')) as $row) {
            list($sku, $qty) = explode(':', $row);
            $data[$sku] = $qty;
        }
        return $data;
    }

    /**
     * Prepare Data For Update
     *
     * @param array $rowData
     * @return array
     */
    protected function _prepareDataForUpdate(array $rowData)
    {
        $toCreate = [];
        $toUpdate = [];

        list($createdAt, $updatedAt) = $this->_prepareDateTime($rowData);
        /* auto generate shipment and order ids */
        if (!empty($rowData[self::COLUMN_SHIPMENT_INCREMENT_ID])) {
            $shipmentId = $this->_getExistShipmentId($rowData);
            if (empty($this->shipmentIdsMap[$shipmentId])) {
                $this->shipmentIdsMap[$shipmentId] = $shipmentId;
            }
            $rowData[self::COLUMN_SHIPMENT_ID] = $shipmentId;

            if (empty($rowData[self::COLUMN_ORDER_ID])) {
                $orderId = $this->_getOrderIdByShipment($rowData);
                if (empty($this->orderIdsMap[$orderId])) {
                    $this->orderIdsMap[$orderId] = $orderId;
                }
                $rowData[self::COLUMN_ORDER_ID] = $orderId;
            }

            if (empty($this->shipmentIdsMap[$shipmentId])) {
                $this->shipmentIdsMap[$shipmentId] = $shipmentId;
            }
            $rowData[self::COLUMN_SHIPMENT_ID] = $shipmentId;
        }

        if (!empty($this->_currentOrderId)) {
            $itemsToShip = [];
            $itemsToInvoice = [];
            $order = null;
            /* create order */
            if ((!empty($this->_parameters['generate_shipment_by_track']) ||
                !empty($this->_parameters['generate_invoice_by_track']))
            ) {
                $order = $this->orderRepository->get(
                    $this->_getExistOrderId()
                );

                /* check if the column of the skus is empty or not */
                if (!empty($rowData['skus'])) {
                    $data = $this->getSkus($rowData['skus']);
                }
                foreach ($order->getAllVisibleItems() as $item) {

                    if (!isset($data[$item->getSku()]) && !empty($rowData['skus'])) {
                        continue;
                    }

                    if ($item->canShip()) {
                        /* if sku columns not empty, it takes the minimum qty */
                        if (!empty($rowData['skus'])) {
                            $qty = min($data[$item->getSku()], $item->getQtyToShip());
                        } else {
                            $qty = $item->getQtyToShip();
                        }
                        if (0 < $qty) {
                            $itemsToShip[$item->getId()] = $qty;
                        }
                    }

                    if ($item->canInvoice()) {
                        /* if sku columns not empty, it takes the minimum qty */
                        if (!empty($rowData['skus'])) {
                            $qty = min($data[$item->getSku()], $item->getQtyToInvoice());
                        } else {
                            $qty = $item->getQtyToInvoice();
                        }
                        if (0 < $qty) {
                            $itemsToInvoice[$item->getId()] = $qty;
                        }
                    }
                }
            }

            /* create shipment */
            if (!empty($this->_parameters['generate_shipment_by_track']) &&
                (empty($rowData[self::COLUMN_SHIPMENT_INCREMENT_ID]) && empty($rowData[self::COLUMN_SHIPMENT_ID])) &&
                0 < count($itemsToShip)
            ) {
                $shipment = $this->shipmentFactory->create($order, $itemsToShip);
                if ($shipment->getTotalQty()) {
                    $shipment->register();

                    $transaction = $this->transactionFactory->create();
                    $transaction->addObject(
                        $shipment
                    )->addObject(
                        $order
                    )->save();

                    $this->addLogWriteln(
                        __('generate shipment with id %1', $shipment->getIncrementId()),
                        $this->output,
                        'info'
                    );

                    $shipmentId = $shipment->getId();
                    if (empty($this->shipmentIdsMap[$shipmentId])) {
                        $this->shipmentIdsMap[$shipmentId] = $shipmentId;
                    }
                    $rowData[self::COLUMN_SHIPMENT_ID] = $shipmentId;
                }
            }

            /* create invoice */
            if (!empty($this->_parameters['generate_invoice_by_track']) &&
                0 < count($itemsToInvoice)
            ) {
                $invoice = $order->prepareInvoice($itemsToInvoice);
                if ($invoice->getTotalQty()) {
                    $invoice->register();

                    $transaction = $this->transactionFactory->create();
                    $transaction->addObject(
                        $invoice
                    )->addObject(
                        $order
                    )->save();

                    $this->addLogWriteln(
                        __('generate invoice with id %1', $invoice->getIncrementId()),
                        $this->output,
                        'info'
                    );
                }
            }

            if (count($itemsToShip) > 0 && count($itemsToInvoice) > 0) {
                //change order status
                if (isset($rowData['status']) && !empty($rowData['status'])) {
                    $order->setData('status', $rowData['status']);
                    $order->setData('state', $rowData['status']);
                    $order->save();
                }
            }
        }

        if (empty($rowData[self::COLUMN_SHIPMENT_ID])) {
            return [
                self::ENTITIES_TO_CREATE_KEY => [],
                self::ENTITIES_TO_UPDATE_KEY => []
            ];
        }

        $newEntity = false;
        $entityId = $this->_getExistEntityId($rowData);
        if (!$entityId) {
            /* create new entity id */
            $newEntity = true;
            $entityId = $this->_getNextEntityId();
            $key = $rowData[self::COLUMN_ENTITY_ID] ?? $entityId;
            $this->_newEntities[$key] = $entityId;
        }

        $entityRow = [
            'created_at' => $createdAt,
            'updated_at' => $updatedAt,
            'entity_id' => $entityId,
            'parent_id' => $this->_getShipmentId($rowData),
            'order_id' => $this->_getExistOrderId()
        ];

        /* prepare data */
        $entityRow = $this->_prepareEntityRow($entityRow, $rowData);
        if ($newEntity) {
            $toCreate[] = $entityRow;
        } else {
            $toUpdate[] = $entityRow;
        }
        return [
            self::ENTITIES_TO_CREATE_KEY => $toCreate,
            self::ENTITIES_TO_UPDATE_KEY => $toUpdate
        ];
    }

    /**
     * Validate Row Data For Add/Update Behaviour
     *
     * @param array $rowData
     * @param int $rowNumber
     * @return void
     */
    protected function _validateRowForUpdate(array $rowData, $rowNumber)
    {
        if (!empty($rowData[self::COLUMN_SHIPMENT_INCREMENT_ID])) {
            /* check there is real shipment */
            if (!$this->_getExistShipmentId($rowData)) {
                $this->addRowError(self::ERROR_SHIPMENT_INCREMENT_ID, $rowNumber);
            }
        } elseif ($this->isShortFormat($rowData)) {
            /* check there is real order */
            if (!empty($rowData['skus']) && !$this->_getExistOrderId()) {
                $this->addRowError(self::ERROR_ORDER_INCREMENT_ID, $rowNumber);
            }

            $shipmentCount = $this->_getShipmentCount();
            if ((empty($rowData[self::COLUMN_SHIPMENT_INCREMENT_ID]) && empty($rowData[self::COLUMN_SHIPMENT_ID])) &&
                1 < $shipmentCount) {
                $this->addRowError(self::ERROR_SHIPMENT_COUNT, $rowNumber);
            }
        } elseif ($this->_checkEntityIdKey($rowData, $rowNumber)) {
            if (empty($rowData[self::COLUMN_SHIPMENT_ID])) {
                $this->addRowError(self::ERROR_SHIPMENT_ID_IS_EMPTY, $rowNumber);
            }

            if (empty($rowData[self::COLUMN_ORDER_ID])) {
                $this->addRowError(self::ERROR_ORDER_ID_IS_EMPTY, $rowNumber);
            }
        }

        if (empty($rowData[self::COLUMN_TRACK_NUMBER])) {
            $this->addRowError(self::ERROR_TRACK_NUMBER_IS_EMPTY, $rowNumber);
        }

        if (!empty($rowData['skus'])) {
            if (false === strpos($rowData['skus'], ':')) {
                $this->addRowError(self::ERROR_SKUS_INCORRECT, $rowNumber);
            } else {
                $data = $this->getSkus($rowData['skus']);
                foreach ($data as $sku => $qty) {
                    if (!is_numeric($qty) || 1 > $qty) {
                        $this->addRowError(self::ERROR_QTY_INCORRECT, $rowNumber, $sku);
                    }
                    if (!$this->getProductIdBySku($sku)) {
                        $this->addRowError(self::ERROR_SKU_NOT_FOUND, $rowNumber, $sku);
                    }
                }
            }
        }
    }

    /**
     * check if data is short format
     *
     * @param array $rowData
     * @return bool
     */
    protected function isShortFormat(array $rowData)
    {
        if (empty($this->_currentOrderId)) {
            return false;
        }
        return !empty($rowData['skus']) || !empty($rowData['track_number']);
    }

    /**
     * Retrieve Shipment Id If Shipment Is Present In Database
     *
     * @param array $rowData
     * @return bool|int
     */
    protected function _getExistShipmentId(array $rowData)
    {
        $bind = [':increment_id' => $rowData[self::COLUMN_SHIPMENT_INCREMENT_ID]];
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from($this->getShipmentTable(), 'entity_id')
            ->where('increment_id = :increment_id');

        return $this->_connection->fetchOne($select, $bind);
    }

    /**
     * Retrieve Order Id If Order Is Present In Database
     *
     * @return bool|int
     */
    protected function _getExistOrderId()
    {
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from($this->getOrderTable(), 'entity_id')
            ->where('increment_id = ?', $this->_currentOrderId);

        return $this->_connection->fetchOne($select);
    }

    /**
     * Retrieve Order Id If Shipment Is Present In Database
     *
     * @param array $rowData
     * @return bool|int
     */
    protected function _getOrderIdByShipment(array $rowData)
    {
        $bind = [':increment_id' => $rowData[self::COLUMN_SHIPMENT_INCREMENT_ID]];
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from($this->getShipmentTable(), 'order_id')
            ->where('increment_id = :increment_id');

        return $this->_connection->fetchOne($select, $bind);
    }

    /**
     * Retrieve count Shipment of order
     *
     * @return bool|int
     */
    protected function _getShipmentCount()
    {
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from(['s' => $this->getShipmentTable()], 'COUNT(*)')
            ->join(
                ['o' => $this->getOrderTable()],
                'o.entity_id = s.order_id',
                []
            )->where('o.increment_id = ?', $this->_currentOrderId);

        return $this->_connection->fetchOne($select);
    }

    /**
     * Retrieve Shipment Id by order
     *
     * @return bool|int
     */
    protected function _getShipmentIdByOrder()
    {
        /** @var $select \Magento\Framework\DB\Select */
        $select = $this->_connection->select();
        $select->from(['s' => $this->getShipmentTable()], 's.entity_id')
            ->join(
                ['o' => $this->getOrderTable()],
                'o.entity_id = s.order_id',
                []
            )->where('o.increment_id = ?', $this->_currentOrderId);

        return $this->_connection->fetchOne($select);
    }

    /**
     * Update And Insert Data In Entity Table
     *
     * @param array $toCreate Rows for insert
     * @param array $toUpdate Rows for update
     * @return $this
     */
    protected function _saveEntities(array $toCreate, array $toUpdate)
    {
        parent::_saveEntities($toCreate, $toUpdate);
        if ($this->_parameters['send_email']) {
            $this->_sendEmail(array_column($toCreate, 'parent_id'));
        }
        return $this;
    }

    /**
     * Send emails
     *
     * @param array $shipmentIds
     * @return $this
     */
    protected function _sendEmail(array $shipmentIds)
    {
        $this->addLogWriteln(__('Sending emails.'), $this->output, 'info');
        $collection = $this->getShipmentCollection()
            ->addFieldToFilter('entity_id', ['in' => array_unique($shipmentIds)]);

        try {
            /** @var \Magento\Sales\Model\Order\Shipment $shipment */
            foreach ($collection as $shipment) {
                $shipment->setEmailSent(true);
                $this->getNotifier()->notify($shipment);
            }
        } catch (\Exception $e) {
            $this->addLogWriteln(__('An error occurred while sending emails.'), $this->output, 'error');
            $this->_logger->critical($e);
        }
        return $this;
    }

    /**
     * Retrieve shipment collection
     *
     * @return \Magento\Sales\Model\ResourceModel\Order\Shipment\Collection
     */
    public function getShipmentCollection()
    {
        if (!$this->shipmentCollection) {
            $this->shipmentCollection = $this->shipmentCollectionFactory->create();
        }
        return $this->shipmentCollection;
    }

    /**
     * Retrieve shipment notifier
     *
     * @return \Magento\Shipping\Model\ShipmentNotifier
     */
    public function getNotifier()
    {
        if (!$this->notifier) {
            $this->notifier = $this->notifierFactory->create();
        }
        return $this->notifier;
    }
}