Question

i am trying to get tracking from csv and save to magento 2 order programmatically. i grabbed the create shipment & add tracking code from here https://www.scommerce-mage.com/blog/magento-2-how-to-create-shipment-programatically.html, and add an execute function (line 142) to load data from csv. but i'm not sure how to create an shipment object (line 179).

<?php
$file = fopen('track.csv', 'r', '"'); // set path to the CSV file
if ($file !== false)
{
    require __DIR__ . '/app/bootstrap.php';
    $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
    $objectManager = $bootstrap->getObjectManager();
    $state = $objectManager->get('Magento\Framework\App\State');
    $state->setAreaCode('adminhtml');
    // add logging capability
    $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/import-update.log');
    $logger = new \Zend\Log\Logger();
    $logger->addWriter($writer);


    class ShipmentObject{
        /*There are two main functions 
        prepareShipment of \Magento\Sales\Model\Order\ShipmentFactory class which prepare invoice and 
        addObject of \Magento\Framework\DB\TransactionFactory class which helps to create shipment and associate the shipment with original order in Magento 2.
        */

        /**
        * @var \Magento\Sales\Model\Order\Shipment\TrackFactory
        */
        protected $_shipmentTrackFactory;

        /**
        * The ShipmentFactory is used to create a new Shipment.
        *
        * @var \Magento\Sales\Model\Order\ShipmentFactory
        */
        protected $_shipmentFactory;

        /**
         * @var \Magento\Framework\DB\TransactionFactory
         */
        protected $_transactionFactory;

        /**
        * @var \Magento\Sales\Api\OrderRepositoryInterface
        */
        protected $_orderRepository;

        protected $_file;
        /**
        * @param \Magento\Sales\Model\Order\Shipment\TrackFactory $shipmentTrackFactory
        * @param \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory
        * @param \Magento\Framework\DB\TransactionFactory $transactionFactory
        * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
        */
        public function __construct(
            \Magento\Sales\Model\Order\Shipment\TrackFactory $shipmentTrackFactory,
            \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory,
            \Magento\Framework\DB\TransactionFactory $transactionFactory,
            \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
            \Magento\Sales\Model\OrderFactory $orderFactory
            ) {
              $this->_shipmentTrackFactory = $shipmentTrackFactory;
              $this->_shipmentFactory = $shipmentFactory;
              $this->_transactionFactory = $transactionFactory;
              $this->_orderRepository = $orderRepository;
              $this->orderFactory = $orderFactory;
        }
        /**
         * @param int $orderId
         * @param string $trackingNumber
         * @return \Magento\Sales\Model\Shipment $shipment
         */
        protected function createShipment($orderId, $trackingNumber)
        {
            try {
                //$order = $this->_orderRepository->get($orderId); // this is increment id
                $order = $this->orderFactory->create()->loadByIncrementId($orderId);

                if ($order){
                    $data = array(array(
                        'carrier_code' => 'fedex', //$order->getShippingMethod(), //'carrier_code' => 'fedex', // need to lower case 
                        'title' => 'Federal Express', //$order->getShippingDescription(),   //'title' => 'Federal Express',  // how to set here 'Federal Express' or 'United Parcel Service'
                        'number' => $trackingNumber,                   //$trackingnumber,
                    ));         

                    $shipment = $this->prepareShipment($order, $data);
                    /*
                    if ($shipment) {
                        $order->setIsInProcess(true);
                        $order->addStatusHistoryComment('Automatically SHIPPED', false);
                        $transactionSave =  $this->_transactionFactory->create()->addObject($shipment)->addObject($shipment->getOrder());
                        $transactionSave->save();
                    }
                    */
                    if ($shipment) {
                       $order->setIsInProcess(true);
                       $order->addStatusHistoryComment('Automatically SHIPPED', false);
                       $transactionSave =  $this->_transactionFactory->create();
                       $transactionSave->addObject($shipment);
                       $transactionSave->addObject($shipment->getOrder());

                       $transactionSave->save();
                    }

                    //return $shipment; //original
                    //return $shipment->save(); //magefms
                    return $order; //helgeB
                }
            } catch (\Exception $e) {
                throw new \Magento\Framework\Exception\LocalizedException(
                    __($e->getMessage())
                );
            }
        }

        /**
        * @param $order \Magento\Sales\Model\Order
        * @param $track array
        * @return $this
        */
        protected function prepareShipment($order, $track)
        {
           $shipment = $this->_shipmentFactory->create(
               $order,
               $this->prepareShipmentItems($order),
               $track
           );
           return $shipment->getTotalQty() ? $shipment->register() : false;
        }

        /**
        * @param $order \Magento\Sales\Model\Order
        * @return array
        */
        protected function prepareShipmentItems($order)
        {
           $items = [];

           foreach($order->getAllItems() as $item) {
               $items[$item->getItemId()] = $item->getQtyOrdered();
           }
           return $items;
        }
        //after prepareShipmentItems function
        /**
        * @param $file
        */
        public function setFile($file)
        {
            $this->_file = $file;
        }

        /**
        * @param null
        * @return ordersave
        */
        public function execute()
        {
        // enter the number of data fields you require the product row inside the CSV file to contain

            $required_data_fields = 3;  //number of column in csv
            //$header = fgetcsv($file); // get data headers and skip 1st row

            while (($row = fgetcsv($this->_file, 100, ",")) !== FALSE)
            //while (($row = fgetcsv($file, 100, ",")) !== FALSE)   
            //while ( $row = fgetcsv($file, 100, ",") )   
            //while (($row = fgetcsv($file)) !== FALSE)
            {
                $data_count = count($row);
                if ($data_count < 1) {
                    continue;
                }

                $data = array();
                //$data = array_combine($header, $row);
                $data = array($row);

                $ponumber = $row[0]; //$data['ponumber']; // column A
                if ($data_count < $required_data_fields) {
                    $logger->info("Skipping Order Number " . $ponumber . ". Not enough data to import.");
                    continue;
                }
                //$shipvia = trim($data[1]); //trim($data['shipvia']);  // column B
                //$trackingnumber = trim($data[2]);//trim($data['trackingnumber']); // column C
                $shipvia = isset($row[1]) ? $row[1] : null;
                $trackingnumber =  isset($row[2]) ? $row[2] : null;

                switch ($shipvia) {
                    case "FEDEX":
                        $shipvia = "fedex";
                        break;
                    case "UPS":
                        $shipvia = "ups";
                        break;
                    case "USPS":
                        $shipvia = "usps";
                        break;
                    default:
                        $shipvia = "fedex";
                }

                ///////////////////////////////start adding tracking///////////////////////////////
                echo 'Updating Order: '.$ponumber.', with '.$shipvia.':'.$trackingnumber."\r\n";
                //print_r($row);

                $order = $this->createShipment($ponumber, $trackingnumber); /* @param int $orderId@param | @param string $trackingNumber */

                if ($order){   //HelgeB
                $order->save();
                }
                ///////////////////////////////end of  adding tracking///////////////////////////////   
                //magefms return $order->save();

            }// end of while loop

        }//end of execute function  

    } // end of class shipmentobject

    /**
    * @param \Magento\Sales\Model\Order\Shipment\TrackFactory $shipmentTrackFactory
    * @param \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory
    * @param \Magento\Framework\DB\TransactionFactory $transactionFactory
    * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
    */
    $shipmentTrackFactory = $objectManager->get('\Magento\Sales\Model\Order\Shipment\TrackFactory');
    $shipmentFactory = $objectManager->get('\Magento\Sales\Model\Order\ShipmentFactory');
    $transactionFactory = $objectManager->get('\Magento\Framework\DB\TransactionFactory');
    $orderRepository = $objectManager->get('\Magento\Sales\Api\OrderRepositoryInterface');
    $orderFactory = $objectManager->get('\Magento\Sales\Model\OrderFactory');

    //create new class object
    $test = new ShipmentObject($shipmentTrackFactory,$shipmentFactory,$transactionFactory,$orderRepository,$orderFactory);
    $test->setFile($file);
    $test->execute();
    fclose($file); 
}// if file not exist, then do nothing

new error tracking information does not copy to (shipment tab) "sales_shipment_grid"

it is showing like this in admin panel, the shipment tab is empty, though the tracking is in there with no carrier assign.

enter image description here enter image description here enter image description here

Was it helpful?

Solution

You will have to get the objects you need in the constructor of your ShipmentObject using the object manager and pass them as parameter.

Your code should look like this from line 175:

$shipmentTrackFactory = $objectManager->get('Magento\Sales\Model\Order\Shipment\TrackFactory');
$shipmentFactory = $objectManager->get('Magento\Sales\Model\Order\ShipmentFactory');
$transactionFactory = $objectManager->get('Magento\Framework\DB\TransactionFactory');
$orderRepository = $objectManager->get('Magento\Sales\Api\OrderRepositoryInterface');


$test = new ShipmentObject($shipmentTrackFactory,$shipmentFactory,$transactionFactory,$orderRepository);

But you will also have to pass the variable $file to the object in order to access it in the execute method. You can either extend the constructor and add a 5th parameter or implement a setter and call it like this:

protected $_file;
[...]
public function setFile($file)
{
    $this->_file = $file;
}
[...]
public function execute()
{
    [...]
    while (($row = fgetcsv($this->_file, 100, ",")) !== FALSE)
    [...]
}
[...]
$test = new ShipmentObject($shipmentTrackFactory,$shipmentFactory,$transactionFactory,$orderRepository);
$test->setFile($file);
$test->execute();

Update to your comment:

Change your method createShipment to return the order object if an order is found or false if not:

protected function createShipment($orderId, $trackingNumber)
{
    [...]
        if ($order){
        [...]
            return $order;
        }
        return false;
    [...]
}

Change your execute method to save the order only if an order object is returned and remove return inside the execute method:

public function execute()
    {
    [...]
        while (($row = fgetcsv($this->_file, 100, ",")) !== FALSE)
        {
        [...]
            $order = $this->createShipment($ponumber, $trackingnumber); /* @param int $orderId@param | @param string $trackingNumber */

            if ($order){   
                $order->save();
            }
        }// end of while loop

    }//end of execute function 

OTHER TIPS

With the provided error, you can try to change this line

if ($shipment) {
   $order->setIsInProcess(true);
   $order->addStatusHistoryComment('Automatically SHIPPED', false);
   $transactionSave =  $this->_transactionFactory->create();
   $transactionSave->addObject($shipment);
   $transactionSave->addObject($shipment->getOrder());

   $transactionSave->save();
}

You should save objects in a separate line.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top