5

I'm running test orders on my almost ready Magento store but faced a very strange problem: I cannot save shipments in admin.

So there are some test orders I made myself as a guest and a signed-in customer. The payment process goes fine through. When I'm on the admin side I can invoice the orders normally, but when I select Ship > Submit Shipment I get this error "Cannot save shipment.".

However, if I scroll down to the orders provided in the Magento sample data, there are some orders that are in the state of Processing - when I follow the aforementioned procedure to ship those, the shippings are created normally and everything works fine.

Same problem occurs regardless of Payment Method or Shipping Method used. However, the sample data orders use Payment & Shipping Methods that are already disabled in the front-end, but that hasn't got any effect: the shipment creation succeeds for sample orders.

What on Earth causes this kind of behaviour? What can I do? Can there be something wrong with the products or Shipping Methods? Pls help me out.

Update 1:

I now edited the function canShip() at app/code/core/Mage/Sales/Model/Order.php

public function canShip()
{
    return true;/*FOR DEBUGGING PURPOSES*/

    /*
    if ($this->canUnhold() || $this->isPaymentReview()) {
        return false;
    }

    if ($this->getIsVirtual() || $this->isCanceled()) {
        return false;
    }

    if ($this->getActionFlag(self::ACTION_FLAG_SHIP) === false) {
        return false;
    }

    foreach ($this->getAllItems() as $item) {
        if ($item->getQtyToShip()>0 && !$item->getIsVirtual()
            && !$item->getLockedDoShip())
        {
            return true;
        }
    }
    return false;
    */
}

But still I got the same results. I flushed Magento cache after making the changes. Then I also tried to set the function to return: false; for every run but still I was able to ship the older orders (magento sample data's pending/processing orders).

Minor Update to above: So now it appears that canShip() function returns true because when I set it to return false; for everything, the orders are just passed through to complete state without the need to Ship them in Admin. I didn't flush caches etc. properly enough yesterday.

UPDATE 2:

I managed to track down the problem a bit further: On app/code/core/Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php line 238 an Exception gets caught (catch (Exception $e)), which results in the error msg "Cannot save shipment" on admin panel.

This is what the function tries to do:

public function saveAction()
{
    $data = $this->getRequest()->getPost('shipment');
    if (!empty($data['comment_text'])) {
        Mage::getSingleton('adminhtml/session')->setCommentText($data['comment_text']);
    }

    try {
        $shipment = $this->_initShipment();
        if (!$shipment) {
            $this->_forward('noRoute');
            return;
        }

        $shipment->register();
        $comment = '';
        if (!empty($data['comment_text'])) {
            $shipment->addComment(
                $data['comment_text'],
                isset($data['comment_customer_notify']),
                isset($data['is_visible_on_front'])
            );
            if (isset($data['comment_customer_notify'])) {
                $comment = $data['comment_text'];
            }
        }

        if (!empty($data['send_email'])) {
            $shipment->setEmailSent(true);
        }

        $shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));
        $responseAjax = new Varien_Object();
        $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label'];

        if ($isNeedCreateLabel && $this->_createShippingLabel($shipment)) {
            $responseAjax->setOk(true);
        }

        $this->_saveShipment($shipment);

        $shipment->sendEmail(!empty($data['send_email']), $comment);

        $shipmentCreatedMessage = $this->__('The shipment has been created.');
        $labelCreatedMessage    = $this->__('The shipping label has been created.');

        $this->_getSession()->addSuccess($isNeedCreateLabel ? $shipmentCreatedMessage . ' ' . $labelCreatedMessage
            : $shipmentCreatedMessage);
        Mage::getSingleton('adminhtml/session')->getCommentText(true);
    } [...etc...]

Then comes the catch part of the function.

Exception log:

exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '400000001' for key 'UNQ_SALES_FLAT_SHIPMENT_INCREMENT_ID'' in [my shop path]/lib/Zend/Db/Statement/Pdo.php:228

Next exception 'Zend_Db_Statement_Exception' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '400000001' for key 'UNQ_SALES_FLAT_SHIPMENT_INCREMENT_ID'' in [my shop path]/lib/Zend/Db/Statement/Pdo.php:234

(Note: "[my shop path]" is added by me, it's not quoted from the exception message.)

UPDATE 4: FOUND THE SOLUTION

After googling the exception log messages and error codes, I found an issue on StackOverflow which was pretty similar. I write it down here since it's not 100 % the same in my case.

  1. Admin > Sales > Orders: Look up the highest order number (for each store view) and write it down. e.g. 100000054, which means that there are 54 placed orders in the system.
  2. Admin > System > Manage Stores: Hover your mouse over your Store View Names to see their ID:s. Write them down. e.g. ID for Store View English is usually 1.
  3. Open your database in phpMyAdmin or your preferred db admin tool.
  4. Open the table eav_entity_type which tells you what is the id number for different entities. Now we are interested in rows with entity_type_code is order and shipment (and maybe also invoice and creditmemo, if your problem is related to those. My problem was about shipping). Write down the entity_type_id values of those rows. In my case they were 5for order and 8for shipment.
  5. Open the table eav_entity_store. Look for rows that match the entity_type_ids of order and shipment AND also match your Store View ID:s. Now you can change the value of increment_last_id to your last actual order number. Since my store isn't open yet, I played it safe and gave a value much bigger for both order and shipment related rows. e.g. if the increment_last_ids were 100000053 for orders and 100000040 for shipments, I gave them both the same new value 100000100 to start over from a clean table. N.B. In my case there was no a row with my local store view and correct shipment entity_type_id. So I copied the row of store view id 1, gave it new values (store id -> 4 and also increment_prefix to match store_id -> 4, and of course increment_last_id -> 400000100) and saved it as a new row in the table.

And that's it. I've now placed a few test orders and everything seems to work. I was also able to ship some older test orders that were lagging behind in Processing mode. That resulted in the fact that order ID:s and shipment ID:s aren't running side-by-side, but they wouldn't do that for long anyway so it's not a problem.

Reflecion:

If I understood this correctly, I think my problem was that for shipment my increment_last_id was too small and the system tried to create a new shipment with an ID that already existed in the database.

The reason why I was able to ship the magento sample data's orders was that they had been done in English Store View, which apparently wasn't corrupted. I'm not sure how my native Store View first ran into the problem – what made the increment_last_id drop back. My guess is that since I'm running 4 different environments for testing Magento, there has happened something that I've different amounts of test purchases in different environments and there has occurred some kind of a version control issue. Dunno 'bout that.

1
  • Many thanks for your answer - a life saver! I have posted my answer below as a reference for others, it adds nothing new, just the SQL that worked for me. Commented Oct 5, 2015 at 9:36

3 Answers 3

1

Anssi,Magento shipment depends on canShip function of Order.php(Mage_Sales_Model_Order)

 public function canShip()
    {
        if ($this->canUnhold() || $this->isPaymentReview()) {
            return false;
        }

        if ($this->getIsVirtual() || $this->isCanceled()) {
            return false;
        }

        if ($this->getActionFlag(self::ACTION_FLAG_SHIP) === false) {
            return false;
        }

        foreach ($this->getAllItems() as $item) {
            if ($item->getQtyToShip()>0 && !$item->getIsVirtual()
                && !$item->getLockedDoShip())
            {
                return true;
            }
        }
        return false;
    }

Explanation:

Condition1: if ($this->canUnhold() || $this->isPaymentReview()) means if Order state code is unhold or order state code is payment_review you cannot create shipment.

Condition2: if ($this->isCanceled() || $this->getIsVirtual() ) {

If order state is cancel or order is virtual means ,you have buy a virtual product,you can not do a shipment

Condition 3:

 if ($this->getActionFlag(self::ACTION_FLAG_SHIP) === false) {
If current action is ship then you can create Shipment

Condition4:

if($item->getQtyToShip()>0 && !$item->getIsVirtual()
                && !$item->getLockedDoShip())

if order all item qty_to_ship value is greater than 0 and Order is not virtual and all items column locked_do_ship is null (In sales_flat_order_item table)

5
  • Thank you very much! I located the function and edited it to this: public function canShip() { return true;/*FOR DEBUGGING PURPOSES*//* [code here...] } but I still get the same error msg :(
    – Anssi
    Commented Aug 5, 2014 at 10:38
  • I'm really confused. Condition 1: "order state is unhold or order state is payment review" seems unlikely. Condition 2: The orders are not canceled and the orders are Simple products (no Virtual / Downloadable). Condition 3: How to test this? Suppose it's ok... Condition 4: Yes, there are at least 1 simple product in each order, order is not virtual, but how to see if locked_do_Ship === <code>null</code>? I didn't find a table named <code>sales_flat_order_item</code> from the mysql database. Where's the problem, I just don't get it!?
    – Anssi
    Commented Aug 5, 2014 at 13:32
  • where are you got confused??
    – Amit Bera
    Commented Aug 5, 2014 at 13:37
  • Anyway, it seems the code doesn't even go to the function <code>canShip()</code> since there's no difference when I edit the source file and comment away all logic inside the function...
    – Anssi
    Commented Aug 5, 2014 at 13:37
  • I'm confused because I think the function should return true but it doesn't.
    – Anssi
    Commented Aug 5, 2014 at 13:39
0

Thanks to the answer in the question I was able to light up my demo store and get shipments rolling.

For anyone else that has added a store front to the demo store this is what is needed to get shipments working on that extra store front:

INSERT INTO `demo`.`eav_entity_store` (`entity_store_id`, `entity_type_id`, `store_id`, `increment_prefix`, `increment_last_id`) VALUES (NULL, '8', '4', '4', '400000002'), (NULL, '7', '4', '4', '400000002');

Strangely there was no problem with creating the invoice or the actual order on my new 'store id 4' store front.

I did not go for a huge offset ('2') and you can almost certainly go for '1'.

0

(This belongs in a comment but I don't have enough rep)

I just wanted to add that I experienced this problem after moving servers on a live website, which caused the increment_last_id value to get out of sync. The answers here involving setting increment_last_id to a higher value resolved this.

So if you are seeing the error message "cannot save shipment" and you have recently changed infrustructure you are likely in the right place.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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