2

I've read a lot of answers to similar question, however all of them are suggesting to create cart/quote and then call placeOrder() or something similar.

I need to create 'historical' orders. They differ from 'normal' orders:

  1. They shouldn't affect current stocks
  2. They may have items that are currently out of stock or disabled
  3. They may have items prices that are different from current prices
  4. Taxes, shipping prices, totals/subtotals are calculated in the past, so we should apply 'historical' values, and shouldn't let Magento 2 calculate them

Any help/thoughts?

5

I ended up creating order directly, skipping quote/cart.

/* @var $orderFactory \Magento\Sales\Model\OrderFactory */
$order = $orderFactory->create()->setStoreId($storeId);
$order
        ->setGlobalCurrencyCode($currency)
        ->setBaseCurrencyCode($currency)
        ->setStoreCurrencyCode($currency)
        ->setOrderCurrencyCode($currency);

/* @var $orderPaymentRepository \Magento\Sales\Api\OrderPaymentRepositoryInterface */
$orderPayment = $orderPaymentRepository->create();
$method = 'checkmo';
$orderPayment->setMethod($method);
$order->setPayment($orderPayment);

/* @var $customer \Magento\Customer\Model\Customer */
$order
        ->setCustomerId($customer->getId())
        ->setCustomerEmail($customer->getEmail())
        ->setCustomerFirstname($customer->getFirstname())
        ->setCustomerLastname($customer->getLastname())
        ->setCustomerGroupId($customer->getGroupId())
        ->setCustomerIsGuest(0);

/* @var $orderAddressRepository \Magento\Sales\Model\Order\AddressRepository */
$orderAddress = $orderAddressRepository->create();
$orderAddress
        ->setStoreId($storeId)
        ->setAddressType($addressType) // \Magento\Sales\Model\Order\Address::TYPE_BILLING and then \Magento\Sales\Model\Order\Address::TYPE_SHIPPING
        ->setCustomerId($customer->getId())
        ->setPrefix($prefix)
        ->setFirstname($firstName)
        ->setMiddlename($middleName)
        ->setLastname($lastName)
        ->setCompany($company)
        ->setStreet($address)
        ->setCity($city)
        ->setPostcode($postCode)
        ->setTelephone($phone)
        ->setFax($fax)
        ->setCountryId($countryCode)
        ->setRegionId($regionId);
$order->setBillingAddress($orderAddress);
// repeat for shipping address $order->setShippingAddress($orderAddress);

$order
        ->setShippingMethod('flatrate_flatrate')
        ->setShippingDescription('Flat Rate - Fixed');
$order->setCreatedAt(strtotime($orderDateTimeString));

// add order item
/* @var $product \Magento\Catalog\Model\Product */
/* @var $orderItemFactory \Magento\Sales\Model\Order\ItemFactory */
$orderItem = $orderItemFactory->create();
$orderItem
        ->setStoreId($storeId)
        ->setQuoteItemId(0)
        ->setQuoteParentItemId(NULL)
        ->setProductId($product->getId())
        ->setProductType($product->getTypeId())
        ->setQtyBackordered(NULL)
        ->setName($product->getName())
        ->setSku($product->getSku())
        ->setTotalQtyOrdered($qty)
        ->setQtyOrdered($qty)
        ->setPrice($itemPrice)
        ->setBasePrice($itemPrice)
        ->setOriginalPrice($itemPrice)
        ->setBaseOriginalPrice($itemPrice)
        ->setTaxAmount($rowTax)
        ->setBaseTaxAmount($rowTax)
        ->setTaxPercent($rowTaxPercent)
        ->setDiscountAmount($rowDiscount)
        ->setBaseDiscountAmount($rowDiscount)
        ->setRowTotal($rowTotal)
        ->setBaseRowTotal($rowTotal)
        ->setWeight(1)
        ->setIsVirtual(0);
$order->addItem($orderItem);
// repeat for each order item

// set status
$order->setStatus(\Magento\Sales\Model\Order::STATE_PROCESSING); // depends on your needs
$order->setState(\Magento\Sales\Model\Order::STATE_PROCESSING);

// set totals
$order->setBaseGrandTotal($baseGrandTotal);
$order->setGrandTotal($grandTotal);
$order->setBaseSubtotal($baseSubtotal);
$order->setSubtotal($subtotal);
$order->setBaseTaxAmount($baseTaxAmount);
$order->setTaxAmount($taxAmount);
$order->setBaseDiscountAmount($baseDiscountAmount);
$order->setDiscountAmount($discountAmount);
$order->setBaseSubtotalInclTax($baseSubtotalInclTax);
$order->setSubtotalInclTax($subtotalInclTax);
$order->setTotalItemCount($totalItemCount);
$order->setTotalQtyOrdered($totalQtyOrdered);

// set shipping amounts
$order->setShippingAmount($shippingAmount);
$order->setBaseShippingAmount($shippingAmount);
$order->setShippingTaxAmount($shippingTax);
$order->setBaseShippingTaxAmount($shippingTax);
$order->setShippingInclTax($shippingAmount + $shippingTax);
$order->setBaseShippingInclTax($shippingAmount + $shippingTax);

// set total paid if needed
$order->setTotalPaid($paid);
$order->setBaseTotalPaid($paid);
$order->save();
  • 1
    If we open order from admin it's giving fatal error: Uncaught Error: Call to a member function getId() on null in vendor/magento/module-sales/Block/Adminhtml/Order/View/Info.php:227 – Lead Developer Sep 25 '18 at 10:31
1

@Rustem, you can try this code which is used to create historical orders.

error_reporting(1);
ini_set('max_execution_time', 0);
use \Magento\Framework\App\Bootstrap;

require __DIR__ . '/app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$url = \Magento\Framework\App\ObjectManager::getInstance();
$storeManager = $url->get('\Magento\Store\Model\StoreManagerInterface');
$mediaurl = $storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
$state = $objectManager->get('\Magento\Framework\App\State');
$state->setAreaCode('frontend');

$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

$_storeManager = $objectManager->get('\Magento\Store\Model\StoreManagerInterface');
$_product = $objectManager->get('\Magento\Catalog\Model\Product');
$_formkey = $objectManager->get('\Magento\Framework\Data\Form\FormKey');
$quote = $objectManager->get('\Magento\Quote\Model\QuoteFactory');
$quoteManagement = $objectManager->get('\Magento\Quote\Model\QuoteManagement');
$customerFactory = $objectManager->get('\Magento\Customer\Model\CustomerFactory');
$customerRepository = $objectManager->get('\Magento\Customer\Api\CustomerRepositoryInterface');
$orderService = $objectManager->get('\Magento\Sales\Model\Service\OrderService');
$_productRepository = $objectManager->get('\Magento\Catalog\Model\ProductRepository');
$productFactory = $objectManager->get('\Magento\Catalog\Model\ProductFactory');
$_orderRepository = $objectManager->get('\Magento\Sales\Model\Service\InvoiceService');
$_invoiceService = $objectManager->get('\Magento\Sales\Api\OrderRepositoryInterface');
$_transaction = $objectManager->get('\Magento\Framework\DB\Transaction');
$_order = $objectManager->get('\Magento\Sales\Model\Order');
$_convertOrder = $objectManager->get('\Magento\Sales\Model\Convert\Order');
$_resource = $objectManager->get('\Magento\Framework\App\ResourceConnection');
$cartRepositoryInterface = $objectManager->get('\Magento\Quote\Api\CartRepositoryInterface');
$cartManagementInterface = $objectManager->get('\Magento\Quote\Api\CartManagementInterface');


$store = $_storeManager->getStore();
$websiteId = $_storeManager->getStore()->getWebsiteId();
$customer = $customerFactory->create();
$customer->setWebsiteId($websiteId);


$payments = array();

        if ($datum['grand_total'] > 0) {
            $tempOrder = array();
            $tempOrder['currency_id'] = 'USD';
            $tempOrder['email'] = $datum['customer_email'];
            $tempOrder['shipping_address'] = $datum['shipping_address'];
            if (isset($datum['billing_address']))
                $tempOrder['billing_address'] = $datum['billing_address'];


                $tempOrder['grand_total'] = $datum['grand_total'];
                $tempOrder['shipping_amount'] = $datum['shipping_amount'];
                $tempOrder['tax_amount'] = $datum['tax_amount'];
                $tempOrder['shipping_description'] = "Free Shipping";//$datum['shipping_description'];
                $tempOrder['state'] = $datum['state'];
                $tempOrder['status'] = $datum['status'];


                $tempOrder['payments'] = $datum['payments'];

                foreach ($datum['trackings'] as $tracking) {
                    $tempOrder['shipping_description'] = $tracking['carrier_name'];
                }

                $tempOrder['items'] = $datum['items'];
                if ($datum['created_at']) {
                    $tempOrder['created_at'] = $datum['created_at'];
                }

                $orderData = $tempOrder;


                $store = $_storeManager->getStore();
                $websiteId = $_storeManager->getStore()->getWebsiteId();
                $customer = $customerFactory->create();
                $customer->setWebsiteId($websiteId);
                $customer->loadByEmail($orderData['email']);// load customet by email address

                $quote1 = $quote->create(); //Create object of quote
                if (!$customer->getEntityId()) {
                    //If not avilable then create this customer

                    $quote1->setCheckoutMethod('guest')
                        ->setCustomerId(null)
                        ->setCustomerEmail($orderData['email'])
                        ->setCustomerIsGuest(true)
                        ->setCustomerGroupId(0);
                    //$logger->info("Customer not exist " . $orderData['email'] . "\r\n");
                    //continue;
                } else {
                    $customer = $customerRepository->getById($customer->getEntityId());
                    $quote1->assignCustomer($customer); //Assign quote to customer
                }

                // if you have allready buyer id then you can load customer directly


                $quote1->setStore($store); //set store for which you create quote


                $quote1->setCurrency();


                 //add items in quote
                $shipping_method = "";
                foreach ($orderData['items'] as $item) {
                    $product = $productFactory->create();
                    $product->load($product->getIdBySku($item['sku']));


                    if ($product->getId()) {
                        $product->setPrice($item['price']);

                        $product->setTaxClassId(0);
                        $quote1->addProduct($product, $item['qty_ordered']);


                    }
                }


                //Set Address to quote
                if (isset($orderData['billing_address'])) {
                    $quote1->getBillingAddress()->addData($orderData['billing_address']);
                } else {
                    $billing = $orderData['shipping_address'];
                    $billing['address_type'] = 'billing';
                    $quote1->getBillingAddress()->addData($billing);
                }
                $orderData['shipping_address']['p21_freight'] = "FREIGHT FREE";


                $quote1->getShippingAddress()->addData($orderData['shipping_address']);

                // Collect Rates and Set Shipping & Payment Method

                $shippingAddress = $quote1->getShippingAddress();
                $shippingAddress->setCollectShippingRates(true)
                    ->collectShippingRates()
                    ->setShippingMethod('freeshipping_freeshipping'); //shipping method
                // $quote->setPaymentMethod('elementpayment'); //payment method
                $quote1->setInventoryProcessed(false); //not effetc inventory


                $quote1->save(); //Now Save quote and your quote is ready

                $payment_method = "";
                $payment = array("method" => "checkmo");


                    $payment_method = $payment['method'];


                // Set Sales Order Payment
                $quote1->getPayment()->importData($payment);


                // Collect Totals & Save Quote
                $quote1->collectTotals()->save();



                $manual_date = "0000-00-00 00:00:00";
                if (isset($orderData['created_at'])) {
                    //  4/26/2016
                    $date = explode("/", $orderData['created_at']);
                    //print_r($date);
                    $orderCustomData['created_at'] = $date[2] . "-" . str_pad($date[0], 2, "0", STR_PAD_LEFT) . "-" . str_pad($date[1], 2, "0", STR_PAD_LEFT) . " 05:05:05";
                    $manual_date = $orderCustomData['created_at'];
                    //exit;

                }


                $orderCustomData['shipping_amount'] = $orderData['shipping_amount'];
                $orderCustomData['base_shipping_amount'] = $orderData['shipping_amount'];
                $orderCustomData['grand_total'] = $orderData['grand_total'];
                $orderCustomData['base_grand_total'] = $orderData['grand_total'];
                $orderCustomData['tax_amount'] = $orderData['tax_amount'];
                $orderCustomData['base_tax_amount'] = $orderData['tax_amount'];
                $orderCustomData['shipping_description'] = $orderData['shipping_description'];
                $orderCustomData['state'] = 'complete';
                $orderCustomData['status'] = 'complete';

                // Create Order From Quote
                $order = "";
                try {

                    $order = $quoteManagement->submit($quote1, $orderCustomData);


                } catch (Exception $e) {

                }
  • Thanks @iyappan-perumal. Why do you call $quote1->setInventoryProcessed(false) with false value? Doesn't it mean that inventory will be processed because it is not processed yet? – Rustem Zinnatullin Jul 5 '17 at 15:26
  • I also noticed that Magento executes reindexings (stocks, prices). However this just adds an overhead, because no changes in stocks or prices could happen when historical order is created. – Rustem Zinnatullin Jul 5 '17 at 15:34
  • @RustemZinnatullin did you tried creating order by commenting the $quote1->setInventoryProcessed(false) the order created without any issue. – Iyappan Perumal Jul 5 '17 at 18:48
  • Hi @iyappan-perumal, yes I tried - the same behavior. I also tried changing false to true, but got a error on reindexing. In any case I wasn't able to make the solution not to decrease stocks. – Rustem Zinnatullin Jul 7 '17 at 6:45
  • @RustemZinnatullin Just before $quote1->setInventoryProcessed(true) Add below code $itemsForReindex = $this->stockManagement->registerProductsSale($items, $quote->getStore()->getWebsiteId()); $this->itemsForReindex->setItems($itemsForReindex); You will get more info about this from this file github.com/magento/magento2/blob/2.2/app/code/Magento/… – Parth Thummar Jan 17 '18 at 11:05

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.