2

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

1

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 
  • Hi,thanks i'm still trying out the solution, but for now, the tracking number display in scientific notation like this " Array ( [0] => 100007331 [1] => FEDEX [2] => 4.90903E+11 )" how can i fix it? – Kris Wen Apr 8 at 16:57
  • The good part: Your code works, I'm sure the data comes that way from your input file. It looks like the original file was an Excel file with that kind of column formatting. Change the column format in the Excel file to text and check the data before you save it as csv. – HelgeB Apr 8 at 17:04
  • it was mistake on my part, opened it using excel and re-saved it causing that error. it print the file correctly now, but it's not saving to magento order. can you help me check my execute function? i posted my updated code. Thank you. – Kris Wen Apr 8 at 18:21
  • i believe i call the correct function "createShipment" with the correct paremeters ($orderid & $trackingnumber) within the execute function, since the "createShipment" function will call "prepareShipment" then "prepareShipmentItems" – Kris Wen Apr 8 at 20:31
  • Your createShipmentmethod must return the order object, because you call in your execute method $order->save() on the return of createShipment. And please remove the return keyword in your while loop in the execute method. I update also my answer with the changes in the two methods. – HelgeB Apr 9 at 8:53
0

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.

  • 1
    i have update the function with the lines you provided, but still getting error. can you see my updated codes error? i'm not sure if i use this function correctly. basically what i want to achieve is load the tracking number from csv file (#order,carrier,tracking) into magento2 order. – Kris Wen Apr 8 at 20:52
  • when you say load, you mean save tracking number from csv to magento sales order? – fmsthird Apr 8 at 21:04
  • 1
    yes, save from csv to magento sales order. In the csv file i have 3 columns ( ordernumber, carrier, tracking), i want to save them to magento sales order. – Kris Wen Apr 8 at 21:13
  • in your execute function can you add a return $order->save(); – fmsthird Apr 8 at 21:29
  • added the line, still have the same error message. – Kris Wen Apr 8 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.