Esempio n. 1
0
    /**
     * Sets the new state of the given order
     *
     * @param int $new_order_state
     * @param int/object $id_order
     * @param bool $use_existing_payment
     */
    public function changeIdOrderState($new_order_state, $id_order, $use_existing_payment = false)
    {
        if (!$new_order_state || !$id_order) {
            return;
        }
        if (!is_object($id_order) && is_numeric($id_order)) {
            $order = new Order((int) $id_order);
        } elseif (is_object($id_order)) {
            $order = $id_order;
        } else {
            return;
        }
        ShopUrl::cacheMainDomainForShop($order->id_shop);
        $new_os = new OrderState((int) $new_order_state, $order->id_lang);
        $old_os = $order->getCurrentOrderState();
        $is_validated = $this->isValidated();
        // executes hook
        if (in_array($new_os->id, array(Configuration::get('PS_OS_PAYMENT'), Configuration::get('PS_OS_WS_PAYMENT')))) {
            Hook::exec('actionPaymentConfirmation', array('id_order' => (int) $order->id), null, false, true, false, $order->id_shop);
        }
        // executes hook
        Hook::exec('actionOrderStatusUpdate', array('newOrderStatus' => $new_os, 'id_order' => (int) $order->id), null, false, true, false, $order->id_shop);
        if (Validate::isLoadedObject($order) && $new_os instanceof OrderState) {
            // An email is sent the first time a virtual item is validated
            $virtual_products = $order->getVirtualProducts();
            if ($virtual_products && (!$old_os || !$old_os->logable) && $new_os && $new_os->logable) {
                $context = Context::getContext();
                $assign = array();
                foreach ($virtual_products as $key => $virtual_product) {
                    $id_product_download = ProductDownload::getIdFromIdProduct($virtual_product['product_id']);
                    $product_download = new ProductDownload($id_product_download);
                    // If this virtual item has an associated file, we'll provide the link to download the file in the email
                    if ($product_download->display_filename != '') {
                        $assign[$key]['name'] = $product_download->display_filename;
                        $dl_link = $product_download->getTextLink(false, $virtual_product['download_hash']) . '&id_order=' . (int) $order->id . '&secure_key=' . $order->secure_key;
                        $assign[$key]['link'] = $dl_link;
                        if (isset($virtual_product['download_deadline']) && $virtual_product['download_deadline'] != '0000-00-00 00:00:00') {
                            $assign[$key]['deadline'] = Tools::displayDate($virtual_product['download_deadline']);
                        }
                        if ($product_download->nb_downloadable != 0) {
                            $assign[$key]['downloadable'] = (int) $product_download->nb_downloadable;
                        }
                    }
                }
                $customer = new Customer((int) $order->id_customer);
                $links = '<ul>';
                foreach ($assign as $product) {
                    $links .= '<li>';
                    $links .= '<a href="' . $product['link'] . '">' . Tools::htmlentitiesUTF8($product['name']) . '</a>';
                    if (isset($product['deadline'])) {
                        $links .= '&nbsp;' . Tools::htmlentitiesUTF8(Tools::displayError('expires on', false)) . '&nbsp;' . $product['deadline'];
                    }
                    if (isset($product['downloadable'])) {
                        $links .= '&nbsp;' . Tools::htmlentitiesUTF8(sprintf(Tools::displayError('downloadable %d time(s)', false), (int) $product['downloadable']));
                    }
                    $links .= '</li>';
                }
                $links .= '</ul>';
                $data = array('{lastname}' => $customer->lastname, '{firstname}' => $customer->firstname, '{id_order}' => (int) $order->id, '{order_name}' => $order->getUniqReference(), '{nbProducts}' => count($virtual_products), '{virtualProducts}' => $links);
                // If there's at least one downloadable file
                if (!empty($assign)) {
                    Mail::Send((int) $order->id_lang, 'download_product', Mail::l('Virtual product to download', $order->id_lang), $data, $customer->email, $customer->firstname . ' ' . $customer->lastname, null, null, null, null, _PS_MAIL_DIR_, false, (int) $order->id_shop);
                }
            }
            // @since 1.5.0 : gets the stock manager
            $manager = null;
            if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) {
                $manager = StockManagerFactory::getManager();
            }
            $errorOrCanceledStatuses = array(Configuration::get('PS_OS_ERROR'), Configuration::get('PS_OS_CANCELED'));
            // foreach products of the order
            if (Validate::isLoadedObject($old_os)) {
                foreach ($order->getProductsDetail() as $product) {
                    // if becoming logable => adds sale
                    if ($new_os->logable && !$old_os->logable) {
                        ProductSale::addProductSale($product['product_id'], $product['product_quantity']);
                        // @since 1.5.0 - Stock Management
                        if (!Pack::isPack($product['product_id']) && in_array($old_os->id, $errorOrCanceledStatuses) && !StockAvailable::dependsOnStock($product['id_product'], (int) $order->id_shop)) {
                            StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], -(int) $product['product_quantity'], $order->id_shop);
                        }
                    } elseif (!$new_os->logable && $old_os->logable) {
                        ProductSale::removeProductSale($product['product_id'], $product['product_quantity']);
                        // @since 1.5.0 - Stock Management
                        if (!Pack::isPack($product['product_id']) && in_array($new_os->id, $errorOrCanceledStatuses) && !StockAvailable::dependsOnStock($product['id_product'])) {
                            StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], (int) $product['product_quantity'], $order->id_shop);
                        }
                    } elseif (!$new_os->logable && !$old_os->logable && in_array($new_os->id, $errorOrCanceledStatuses) && !in_array($old_os->id, $errorOrCanceledStatuses) && !StockAvailable::dependsOnStock($product['id_product'])) {
                        StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], (int) $product['product_quantity'], $order->id_shop);
                    }
                    // @since 1.5.0 : if the order is being shipped and this products uses the advanced stock management :
                    // decrements the physical stock using $id_warehouse
                    if ($new_os->shipped == 1 && $old_os->shipped == 0 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && Warehouse::exists($product['id_warehouse']) && $manager != null && ((int) $product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) {
                        // gets the warehouse
                        $warehouse = new Warehouse($product['id_warehouse']);
                        // decrements the stock (if it's a pack, the StockManager does what is needed)
                        $manager->removeProduct($product['product_id'], $product['product_attribute_id'], $warehouse, $product['product_quantity'], Configuration::get('PS_STOCK_CUSTOMER_ORDER_REASON'), true, (int) $order->id);
                    } elseif ($new_os->shipped == 0 && $old_os->shipped == 1 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && Warehouse::exists($product['id_warehouse']) && $manager != null && ((int) $product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) {
                        // if the product is a pack, we restock every products in the pack using the last negative stock mvts
                        if (Pack::isPack($product['product_id'])) {
                            $pack_products = Pack::getItems($product['product_id'], Configuration::get('PS_LANG_DEFAULT', null, null, $order->id_shop));
                            foreach ($pack_products as $pack_product) {
                                if ($pack_product->advanced_stock_management == 1) {
                                    $mvts = StockMvt::getNegativeStockMvts($order->id, $pack_product->id, 0, $pack_product->pack_quantity * $product['product_quantity']);
                                    foreach ($mvts as $mvt) {
                                        $manager->addProduct($pack_product->id, 0, new Warehouse($mvt['id_warehouse']), $mvt['physical_quantity'], null, $mvt['price_te'], true);
                                    }
                                    if (!StockAvailable::dependsOnStock($product['id_product'])) {
                                        StockAvailable::updateQuantity($pack_product->id, 0, (int) $pack_product->pack_quantity * $product['product_quantity'], $order->id_shop);
                                    }
                                }
                            }
                        } else {
                            $mvts = StockMvt::getNegativeStockMvts($order->id, $product['product_id'], $product['product_attribute_id'], $product['product_quantity']);
                            foreach ($mvts as $mvt) {
                                $manager->addProduct($product['product_id'], $product['product_attribute_id'], new Warehouse($mvt['id_warehouse']), $mvt['physical_quantity'], null, $mvt['price_te'], true);
                            }
                        }
                    }
                }
            }
        }
        $this->id_order_state = (int) $new_order_state;
        // changes invoice number of order ?
        if (!Validate::isLoadedObject($new_os) || !Validate::isLoadedObject($order)) {
            die(Tools::displayError('Invalid new order state'));
        }
        // the order is valid if and only if the invoice is available and the order is not cancelled
        $order->current_state = $this->id_order_state;
        $order->valid = $new_os->logable;
        $order->update();
        if ($new_os->invoice && !$order->invoice_number) {
            $order->setInvoice($use_existing_payment);
        }
        // set orders as paid
        if ($new_os->paid == 1) {
            $invoices = $order->getInvoicesCollection();
            if ($order->total_paid != 0) {
                $payment_method = Module::getInstanceByName($order->module);
            }
            foreach ($invoices as $invoice) {
                $rest_paid = $invoice->getRestPaid();
                if ($rest_paid > 0) {
                    $payment = new OrderPayment();
                    $payment->order_reference = $order->reference;
                    $payment->id_currency = $order->id_currency;
                    $payment->amount = $rest_paid;
                    if ($order->total_paid != 0) {
                        $payment->payment_method = $payment_method->displayName;
                    } else {
                        $payment->payment_method = null;
                    }
                    // Update total_paid_real value for backward compatibility reasons
                    if ($payment->id_currency == $order->id_currency) {
                        $order->total_paid_real += $payment->amount;
                    } else {
                        $order->total_paid_real += Tools::ps_round(Tools::convertPrice($payment->amount, $payment->id_currency, false), 2);
                    }
                    $order->save();
                    $payment->conversion_rate = 1;
                    $payment->save();
                    Db::getInstance()->execute('
					INSERT INTO `' . _DB_PREFIX_ . 'order_invoice_payment`
					VALUES(' . (int) $invoice->id . ', ' . (int) $payment->id . ', ' . (int) $order->id . ')');
                }
            }
        }
        // updates delivery date even if it was already set by another state change
        if ($new_os->delivery) {
            $order->setDelivery();
        }
        // executes hook
        Hook::exec('actionOrderStatusPostUpdate', array('newOrderStatus' => $new_os, 'id_order' => (int) $order->id), null, false, true, false, $order->id_shop);
        ShopUrl::resetMainDomainCache();
    }
Esempio n. 2
0
 /**
  * Get products grouped by package and by addresses to be sent individualy (one package = one shipping cost).
  *
  * @return array array(
  *                   0 => array( // First address
  *                       0 => array(  // First package
  *                           'product_list' => array(...),
  *                           'carrier_list' => array(...),
  *                           'id_warehouse' => array(...),
  *                       ),
  *                   ),
  *               );
  * @todo Add avaibility check
  */
 public function getPackageList($flush = false)
 {
     static $cache = array();
     if (isset($cache[(int) $this->id . '_' . (int) $this->id_address_delivery]) && $cache[(int) $this->id . '_' . (int) $this->id_address_delivery] !== false && !$flush) {
         return $cache[(int) $this->id . '_' . (int) $this->id_address_delivery];
     }
     $product_list = $this->getProducts();
     // Step 1 : Get product informations (warehouse_list and carrier_list), count warehouse
     // Determine the best warehouse to determine the packages
     // For that we count the number of time we can use a warehouse for a specific delivery address
     $warehouse_count_by_address = array();
     $warehouse_carrier_list = array();
     $stock_management_active = Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT');
     foreach ($product_list as &$product) {
         if ((int) $product['id_address_delivery'] == 0) {
             $product['id_address_delivery'] = (int) $this->id_address_delivery;
         }
         if (!isset($warehouse_count_by_address[$product['id_address_delivery']])) {
             $warehouse_count_by_address[$product['id_address_delivery']] = array();
         }
         $product['warehouse_list'] = array();
         if ($stock_management_active && ((int) $product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement((int) $product['id_product']))) {
             $warehouse_list = Warehouse::getProductWarehouseList($product['id_product'], $product['id_product_attribute'], $this->id_shop);
             if (count($warehouse_list) == 0) {
                 $warehouse_list = Warehouse::getProductWarehouseList($product['id_product'], $product['id_product_attribute']);
             }
             // Does the product is in stock ?
             // If yes, get only warehouse where the product is in stock
             $warehouse_in_stock = array();
             $manager = StockManagerFactory::getManager();
             foreach ($warehouse_list as $key => $warehouse) {
                 $product_real_quantities = $manager->getProductRealQuantities($product['id_product'], $product['id_product_attribute'], array($warehouse['id_warehouse']), true);
                 if ($product_real_quantities > 0 || Pack::isPack((int) $product['id_product'])) {
                     $warehouse_in_stock[] = $warehouse;
                 }
             }
             if (!empty($warehouse_in_stock)) {
                 $warehouse_list = $warehouse_in_stock;
                 $product['in_stock'] = true;
             } else {
                 $product['in_stock'] = false;
             }
         } else {
             //simulate default warehouse
             $warehouse_list = array(0);
             $product['in_stock'] = StockAvailable::getQuantityAvailableByProduct($product['id_product'], $product['id_product_attribute']) > 0;
         }
         foreach ($warehouse_list as $warehouse) {
             if (!isset($warehouse_carrier_list[$warehouse['id_warehouse']])) {
                 $warehouse_object = new Warehouse($warehouse['id_warehouse']);
                 $warehouse_carrier_list[$warehouse['id_warehouse']] = $warehouse_object->getCarriers();
             }
             $product['warehouse_list'][] = $warehouse['id_warehouse'];
             if (!isset($warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']])) {
                 $warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']] = 0;
             }
             $warehouse_count_by_address[$product['id_address_delivery']][$warehouse['id_warehouse']]++;
         }
     }
     unset($product);
     arsort($warehouse_count_by_address);
     // Step 2 : Group product by warehouse
     $grouped_by_warehouse = array();
     foreach ($product_list as &$product) {
         if (!isset($grouped_by_warehouse[$product['id_address_delivery']])) {
             $grouped_by_warehouse[$product['id_address_delivery']] = array('in_stock' => array(), 'out_of_stock' => array());
         }
         $product['carrier_list'] = array();
         $id_warehouse = 0;
         foreach ($warehouse_count_by_address[$product['id_address_delivery']] as $id_war => $val) {
             if (in_array((int) $id_war, $product['warehouse_list'])) {
                 $product['carrier_list'] = array_merge($product['carrier_list'], Carrier::getAvailableCarrierList(new Product($product['id_product']), $id_war, $product['id_address_delivery'], null, $this));
                 if (!$id_warehouse) {
                     $id_warehouse = (int) $id_war;
                 }
             }
         }
         if (!isset($grouped_by_warehouse[$product['id_address_delivery']]['in_stock'][$id_warehouse])) {
             $grouped_by_warehouse[$product['id_address_delivery']]['in_stock'][$id_warehouse] = array();
             $grouped_by_warehouse[$product['id_address_delivery']]['out_of_stock'][$id_warehouse] = array();
         }
         if (!$this->allow_seperated_package) {
             $key = 'in_stock';
         } else {
             $key = $product['in_stock'] ? 'in_stock' : 'out_of_stock';
         }
         if (empty($product['carrier_list'])) {
             $product['carrier_list'] = array(0);
         }
         $grouped_by_warehouse[$product['id_address_delivery']][$key][$id_warehouse][] = $product;
     }
     unset($product);
     // Step 3 : grouped product from grouped_by_warehouse by available carriers
     $grouped_by_carriers = array();
     foreach ($grouped_by_warehouse as $id_address_delivery => $products_in_stock_list) {
         if (!isset($grouped_by_carriers[$id_address_delivery])) {
             $grouped_by_carriers[$id_address_delivery] = array('in_stock' => array(), 'out_of_stock' => array());
         }
         foreach ($products_in_stock_list as $key => $warehouse_list) {
             if (!isset($grouped_by_carriers[$id_address_delivery][$key])) {
                 $grouped_by_carriers[$id_address_delivery][$key] = array();
             }
             foreach ($warehouse_list as $id_warehouse => $product_list) {
                 if (!isset($grouped_by_carriers[$id_address_delivery][$key][$id_warehouse])) {
                     $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse] = array();
                 }
                 foreach ($product_list as $product) {
                     $package_carriers_key = implode(',', $product['carrier_list']);
                     if (!isset($grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key])) {
                         $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key] = array('product_list' => array(), 'carrier_list' => $product['carrier_list'], 'warehouse_list' => $product['warehouse_list']);
                     }
                     $grouped_by_carriers[$id_address_delivery][$key][$id_warehouse][$package_carriers_key]['product_list'][] = $product;
                 }
             }
         }
     }
     $package_list = array();
     // Step 4 : merge product from grouped_by_carriers into $package to minimize the number of package
     foreach ($grouped_by_carriers as $id_address_delivery => $products_in_stock_list) {
         if (!isset($package_list[$id_address_delivery])) {
             $package_list[$id_address_delivery] = array('in_stock' => array(), 'out_of_stock' => array());
         }
         foreach ($products_in_stock_list as $key => $warehouse_list) {
             if (!isset($package_list[$id_address_delivery][$key])) {
                 $package_list[$id_address_delivery][$key] = array();
             }
             // Count occurance of each carriers to minimize the number of packages
             $carrier_count = array();
             foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) {
                 foreach ($products_grouped_by_carriers as $data) {
                     foreach ($data['carrier_list'] as $id_carrier) {
                         if (!isset($carrier_count[$id_carrier])) {
                             $carrier_count[$id_carrier] = 0;
                         }
                         $carrier_count[$id_carrier]++;
                     }
                 }
             }
             arsort($carrier_count);
             foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) {
                 if (!isset($package_list[$id_address_delivery][$key][$id_warehouse])) {
                     $package_list[$id_address_delivery][$key][$id_warehouse] = array();
                 }
                 foreach ($products_grouped_by_carriers as $data) {
                     foreach ($carrier_count as $id_carrier => $rate) {
                         if (in_array($id_carrier, $data['carrier_list'])) {
                             if (!isset($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier])) {
                                 $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier] = array('carrier_list' => $data['carrier_list'], 'warehouse_list' => $data['warehouse_list'], 'product_list' => array());
                             }
                             $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['carrier_list'] = array_intersect($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['carrier_list'], $data['carrier_list']);
                             $package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['product_list'] = array_merge($package_list[$id_address_delivery][$key][$id_warehouse][$id_carrier]['product_list'], $data['product_list']);
                             break;
                         }
                     }
                 }
             }
         }
     }
     // Step 5 : Reduce depth of $package_list
     $final_package_list = array();
     foreach ($package_list as $id_address_delivery => $products_in_stock_list) {
         if (!isset($final_package_list[$id_address_delivery])) {
             $final_package_list[$id_address_delivery] = array();
         }
         foreach ($products_in_stock_list as $key => $warehouse_list) {
             foreach ($warehouse_list as $id_warehouse => $products_grouped_by_carriers) {
                 foreach ($products_grouped_by_carriers as $data) {
                     $final_package_list[$id_address_delivery][] = array('product_list' => $data['product_list'], 'carrier_list' => $data['carrier_list'], 'warehouse_list' => $data['warehouse_list'], 'id_warehouse' => $id_warehouse);
                 }
             }
         }
     }
     $cache[(int) $this->id] = $final_package_list;
     return $final_package_list;
 }
Esempio n. 3
0
    /**
     * Sets the new state of the given order
     *
     * @param int $new_order_state
     * @param int $id_order
     * @param bool $use_existing_payment
     */
    public function changeIdOrderState($new_order_state, &$id_order, $use_existing_payment = false)
    {
        if (!$new_order_state || !$id_order) {
            return;
        }
        if (!is_object($id_order) && is_numeric($id_order)) {
            $order = new Order((int) $id_order);
        } elseif (is_object($id_order)) {
            $order = $id_order;
        } else {
            return;
        }
        $new_os = new OrderState((int) $new_order_state, $order->id_lang);
        $old_os = $order->getCurrentOrderState();
        $is_validated = $this->isValidated();
        // executes hook
        if ($new_os->id == Configuration::get('PS_OS_PAYMENT')) {
            Hook::exec('actionPaymentConfirmation', array('id_order' => (int) $order->id));
        }
        // executes hook
        Hook::exec('actionOrderStatusUpdate', array('newOrderStatus' => $new_os, 'id_order' => (int) $order->id));
        if (Validate::isLoadedObject($order) && $old_os instanceof OrderState && $new_os instanceof OrderState) {
            // @since 1.5.0 : gets the stock manager
            $manager = null;
            if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) {
                $manager = StockManagerFactory::getManager();
            }
            // foreach products of the order
            foreach ($order->getProductsDetail() as $product) {
                // if becoming logable => adds sale
                if ($new_os->logable && !$old_os->logable) {
                    ProductSale::addProductSale($product['product_id'], $product['product_quantity']);
                    // @since 1.5.0 - Stock Management
                    if (!Pack::isPack($product['product_id']) && ($old_os->id == Configuration::get('PS_OS_ERROR') || $old_os->id == Configuration::get('PS_OS_CANCELED')) && !StockAvailable::dependsOnStock($product['id_product'], (int) $order->id_shop)) {
                        StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], -(int) $product['product_quantity'], $order->id_shop);
                    }
                } elseif (!$new_os->logable && $old_os->logable) {
                    ProductSale::removeProductSale($product['product_id'], $product['product_quantity']);
                    // @since 1.5.0 - Stock Management
                    if (!Pack::isPack($product['product_id']) && ($new_os->id == Configuration::get('PS_OS_ERROR') || $new_os->id == Configuration::get('PS_OS_CANCELED')) && !StockAvailable::dependsOnStock($product['id_product'])) {
                        StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], (int) $product['product_quantity'], $order->id_shop);
                    }
                } elseif (!$new_os->logable && !$old_os->logable && ($new_os->id == Configuration::get('PS_OS_ERROR') || $new_os->id == Configuration::get('PS_OS_CANCELED')) && !StockAvailable::dependsOnStock($product['id_product'])) {
                    StockAvailable::updateQuantity($product['product_id'], $product['product_attribute_id'], (int) $product['product_quantity'], $order->id_shop);
                }
                // @since 1.5.0 : if the order is being shipped and this products uses the advanced stock management :
                // decrements the physical stock using $id_warehouse
                if ($new_os->shipped == 1 && $old_os->shipped == 0 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && Warehouse::exists($product['id_warehouse']) && $manager != null && ((int) $product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) {
                    // gets the warehouse
                    $warehouse = new Warehouse($product['id_warehouse']);
                    // decrements the stock (if it's a pack, the StockManager does what is needed)
                    $manager->removeProduct($product['product_id'], $product['product_attribute_id'], $warehouse, $product['product_quantity'], Configuration::get('PS_STOCK_CUSTOMER_ORDER_REASON'), true, (int) $order->id);
                } elseif ($new_os->shipped == 0 && $old_os->shipped == 1 && Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && Warehouse::exists($product['id_warehouse']) && $manager != null && ((int) $product['advanced_stock_management'] == 1 || Pack::usesAdvancedStockManagement($product['product_id']))) {
                    // if the product is a pack, we restock every products in the pack using the last negative stock mvts
                    if (Pack::isPack($product['product_id'])) {
                        $pack_products = Pack::getItems($product['product_id'], Configuration::get('PS_LANG_DEFAULT'));
                        foreach ($pack_products as $pack_product) {
                            if ($pack_product->advanced_stock_management == 1) {
                                $mvts = StockMvt::getNegativeStockMvts($order->id, $pack_product->id, 0, $pack_product->pack_quantity * $product['product_quantity']);
                                foreach ($mvts as $mvt) {
                                    $manager->addProduct($pack_product->id, 0, new Warehouse($mvt['id_warehouse']), $mvt['physical_quantity'], null, $mvt['price_te'], true);
                                }
                                if (!StockAvailable::dependsOnStock($product['id_product'])) {
                                    StockAvailable::updateQuantity($pack_product->id, 0, (int) $pack_product->pack_quantity * $product['product_quantity'], $order->id_shop);
                                }
                            }
                        }
                    } else {
                        $mvts = StockMvt::getNegativeStockMvts($order->id, $product['product_id'], $product['product_attribute_id'], $product['product_quantity']);
                        foreach ($mvts as $mvt) {
                            $manager->addProduct($product['product_id'], $product['product_attribute_id'], new Warehouse($mvt['id_warehouse']), $mvt['physical_quantity'], null, $mvt['price_te'], true);
                        }
                    }
                }
            }
        }
        $this->id_order_state = (int) $new_order_state;
        // changes invoice number of order ?
        if (!Validate::isLoadedObject($new_os) || !Validate::isLoadedObject($order)) {
            die(Tools::displayError('Invalid new order state'));
        }
        // the order is valid if and only if the invoice is available and the order is not cancelled
        $order->current_state = $this->id_order_state;
        $order->valid = $new_os->logable;
        $order->update();
        if ($new_os->invoice && !$order->invoice_number) {
            $order->setInvoice($use_existing_payment);
        }
        // set orders as paid
        if ($new_os->paid == 1) {
            $invoices = $order->getInvoicesCollection();
            if ($order->total_paid != 0) {
                $payment_method = Module::getInstanceByName($order->module);
            }
            foreach ($invoices as $invoice) {
                $rest_paid = $invoice->getRestPaid();
                if ($rest_paid > 0) {
                    $payment = new OrderPayment();
                    $payment->order_reference = $order->reference;
                    $payment->id_currency = $order->id_currency;
                    $payment->amount = $rest_paid;
                    if ($order->total_paid != 0) {
                        $payment->payment_method = $payment_method->displayName;
                    } else {
                        $payment->payment_method = null;
                    }
                    // Update total_paid_real value for backward compatibility reasons
                    if ($payment->id_currency == $order->id_currency) {
                        $order->total_paid_real += $payment->amount;
                    } else {
                        $order->total_paid_real += Tools::ps_round(Tools::convertPrice($payment->amount, $payment->id_currency, false), 2);
                    }
                    $order->save();
                    $payment->conversion_rate = 1;
                    $payment->save();
                    Db::getInstance()->execute('
					INSERT INTO `' . _DB_PREFIX_ . 'order_invoice_payment`
					VALUES(' . (int) $invoice->id . ', ' . (int) $payment->id . ', ' . (int) $order->id . ')');
                }
            }
        }
        // updates delivery date even if it was already set by another state change
        if ($new_os->delivery) {
            $order->setDelivery();
        }
        // executes hook
        Hook::exec('actionOrderStatusPostUpdate', array('newOrderStatus' => $new_os, 'id_order' => (int) $order->id));
    }