diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php
index 6dc7fb8e5..f03bd056e 100644
--- a/classes/PaymentModule.php
+++ b/classes/PaymentModule.php
@@ -214,7 +214,7 @@ abstract class PaymentModuleCore extends Module
$order_list[] = $order;
// Insert new Order detail list using cart for the current order
- $order_detail = new OrderDetail($this->context);
+ $order_detail = new OrderDetail(null, null, $this->context);
$order_detail->createList($order, $cart, $id_order_state, $product_list);
$order_detail_list[] = $order_detail;
}
@@ -243,7 +243,7 @@ abstract class PaymentModuleCore extends Module
}
// Insert new Order detail list using cart for the current order
- //$orderDetail = new OrderDetail($this->context);
+ //$orderDetail = new OrderDetail(null, null, $this->context);
//$orderDetail->createList($order, $cart, $id_order_state);
//$this->addPCC($order->id, $order->id_currency, $amountPaid);
diff --git a/classes/order/Order.php b/classes/order/Order.php
new file mode 100644
index 000000000..4661b08de
--- /dev/null
+++ b/classes/order/Order.php
@@ -0,0 +1,1346 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 7331 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderCore extends ObjectModel
+{
+ /** @var integer Delivery address id */
+ public $id_address_delivery;
+
+ /** @var integer Invoice address id */
+ public $id_address_invoice;
+
+ public $id_group_shop;
+
+ public $id_shop;
+
+ /** @var integer Cart id */
+ public $id_cart;
+
+ /** @var integer Currency id */
+ public $id_currency;
+
+ /** @var integer Language id */
+ public $id_lang;
+
+ /** @var integer Customer id */
+ public $id_customer;
+
+ /** @var integer Carrier id */
+ public $id_carrier;
+
+ /** @var string Secure key */
+ public $secure_key;
+
+ /** @var string Payment method */
+ public $payment;
+
+ /** @var string Payment module */
+ public $module;
+
+ /** @var float Currency conversion rate */
+ public $conversion_rate;
+
+ /** @var boolean Customer is ok for a recyclable package */
+ public $recyclable = 1;
+
+ /** @var boolean True if the customer wants a gift wrapping */
+ public $gift = 0;
+
+ /** @var string Gift message if specified */
+ public $gift_message;
+
+ /** @var string Shipping number */
+ public $shipping_number;
+
+ /** @var float Discounts total */
+ public $total_discounts;
+
+ public $total_discounts_tax_incl;
+ public $total_discounts_tax_excl;
+
+ /** @var float Total to pay */
+ public $total_paid;
+
+ /** @var float Total to pay tax included */
+ public $total_paid_tax_incl;
+
+ /** @var float Total to pay tax excluded */
+ public $total_paid_tax_excl;
+
+ /** @var float Total really paid */
+ public $total_paid_real;
+
+ /** @var float Products total */
+ public $total_products;
+
+ /** @var float Products total tax excluded */
+ public $total_products_wt;
+
+ /** @var float Shipping total */
+ public $total_shipping;
+
+ /** @var float Shipping total tax included */
+ public $total_shipping_tax_incl;
+
+ /** @var float Shipping total tax excluded */
+ public $total_shipping_tax_excl;
+
+ /** @var float Shipping tax rate */
+ public $carrier_tax_rate;
+
+ /** @var float Wrapping total */
+ public $total_wrapping;
+
+ /** @var float Wrapping total tax included */
+ public $total_wrapping_tax_incl;
+
+ /** @var float Wrapping total tax excluded */
+ public $total_wrapping_tax_excl;
+
+ /** @var integer Invoice number */
+ public $invoice_number;
+
+ /** @var integer Delivery number */
+ public $delivery_number;
+
+ /** @var string Invoice creation date */
+ public $invoice_date;
+
+ /** @var string Delivery creation date */
+ public $delivery_date;
+
+ /** @var boolean Order validity (paid and not canceled) */
+ public $valid;
+
+ /** @var string Object creation date */
+ public $date_add;
+
+ /** @var string Object last modification date */
+ public $date_upd;
+
+ /** @var string Order reference
+ * This reference is not unique, but unique for a payment
+ */
+ public $reference;
+
+ /** @var int Id warehouse */
+ public $id_warehouse;
+
+ protected $tables = array ('orders');
+
+ protected $fieldsRequired = array('conversion_rate', 'id_address_delivery', 'id_address_invoice', 'id_cart', 'id_currency', 'id_lang', 'id_customer', 'id_carrier', 'payment', 'total_paid', 'total_paid_real', 'total_products', 'total_products_wt');
+ protected $fieldsValidate = array(
+ 'id_address_delivery' => 'isUnsignedId',
+ 'id_address_invoice' => 'isUnsignedId',
+ 'id_cart' => 'isUnsignedId',
+ 'id_currency' => 'isUnsignedId',
+ 'id_group_shop' => 'isUnsignedId',
+ 'id_shop' => 'isUnsignedId',
+ 'id_lang' => 'isUnsignedId',
+ 'id_customer' => 'isUnsignedId',
+ 'id_carrier' => 'isUnsignedId',
+ 'id_warehouse' => 'isUnsignedId',
+ 'secure_key' => 'isMd5',
+ 'payment' => 'isGenericName',
+ 'recyclable' => 'isBool',
+ 'gift' => 'isBool',
+ 'gift_message' => 'isMessage',
+ 'total_discounts' => 'isPrice',
+ 'total_paid' => 'isPrice',
+ 'total_paid_real' => 'isPrice',
+ 'total_products' => 'isPrice',
+ 'total_products_wt' => 'isPrice',
+ 'total_shipping' => 'isPrice',
+ 'carrier_tax_rate' => 'isFloat',
+ 'total_wrapping' => 'isPrice',
+ 'shipping_number' => 'isUrl',
+ 'conversion_rate' => 'isFloat'
+ );
+
+ protected $webserviceParameters = array(
+ 'objectMethods' => array('add' => 'addWs'),
+ 'objectNodeName' => 'order',
+ 'objectsNodeName' => 'orders',
+ 'fields' => array(
+ 'id_address_delivery' => array('xlink_resource'=> 'addresses'),
+ 'id_address_invoice' => array('xlink_resource'=> 'addresses'),
+ 'id_cart' => array('xlink_resource'=> 'carts'),
+ 'id_currency' => array('xlink_resource'=> 'currencies'),
+ 'id_lang' => array('xlink_resource'=> 'languages'),
+ 'id_customer' => array('xlink_resource'=> 'customers'),
+ 'id_carrier' => array('xlink_resource'=> 'carriers'),
+ 'module' => array('required' => true),
+ 'invoice_number' => array(),
+ 'invoice_date' => array(),
+ 'delivery_number' => array(),
+ 'delivery_date' => array(),
+ 'valid' => array(),
+ 'current_state' => array('getter' => 'getCurrentState', 'setter' => 'setCurrentState', 'xlink_resource'=> 'order_states'),
+ 'date_add' => array(),
+ 'date_upd' => array(),
+ ),
+ 'associations' => array(
+ 'order_rows' => array('resource' => 'order_row', 'setter' => false, 'virtual_entity' => true,
+ 'fields' => array(
+ 'id' => array(),
+ 'product_id' => array('required' => true),
+ 'product_attribute_id' => array('required' => true),
+ 'product_quantity' => array('required' => true),
+ 'product_name' => array('setter' => false),
+ 'product_price' => array('setter' => false),
+ )),
+ ),
+
+ );
+
+ /* MySQL does not allow 'order' for a table name */
+ protected $table = 'orders';
+ protected $identifier = 'id_order';
+ protected $_taxCalculationMethod = PS_TAX_EXC;
+
+ protected static $_historyCache = array();
+
+ public function getFields()
+ {
+ if (!$this->id_lang)
+ $this->id_lang = Configuration::get('PS_LANG_DEFAULT');
+
+ $this->validateFields();
+
+ $fields['id_group_shop'] = (int)$this->id_group_shop;
+ $fields['id_shop'] = (int)$this->id_shop;
+ $fields['id_address_delivery'] = (int)($this->id_address_delivery);
+ $fields['id_address_invoice'] = (int)($this->id_address_invoice);
+ $fields['id_cart'] = (int)$this->id_cart;
+ $fields['id_currency'] = (int)($this->id_currency);
+ $fields['id_lang'] = (int)($this->id_lang);
+ $fields['id_customer'] = (int)($this->id_customer);
+ $fields['id_carrier'] = (int)($this->id_carrier);
+ $fields['secure_key'] = pSQL($this->secure_key);
+ $fields['payment'] = pSQL($this->payment);
+ $fields['module'] = pSQL($this->module);
+ $fields['conversion_rate'] = (float)($this->conversion_rate);
+ $fields['recyclable'] = (int)($this->recyclable);
+ $fields['gift'] = (int)($this->gift);
+ $fields['gift_message'] = pSQL($this->gift_message);
+ $fields['shipping_number'] = pSQL($this->shipping_number);
+ $fields['total_discounts'] = (float)($this->total_discounts);
+ $fields['total_discounts_tax_incl'] = (float)($this->total_discounts_tax_incl);
+ $fields['total_discounts_tax_excl'] = (float)($this->total_discounts_tax_excl);
+ $fields['total_paid'] = (float)($this->total_paid);
+ $fields['total_paid_tax_incl'] = (float)($this->total_paid_tax_incl);
+ $fields['total_paid_tax_excl'] = (float)($this->total_paid_tax_excl);
+ $fields['total_paid_real'] = (float)($this->total_paid_real);
+ $fields['total_products'] = (float)($this->total_products);
+ $fields['total_products_wt'] = (float)($this->total_products_wt);
+ $fields['total_shipping'] = (float)($this->total_shipping);
+ $fields['total_shipping_tax_incl'] = (float)($this->total_shipping_tax_incl);
+ $fields['total_shipping_tax_excl'] = (float)($this->total_shipping_tax_excl);
+ $fields['carrier_tax_rate'] = (float)($this->carrier_tax_rate);
+ $fields['total_wrapping'] = (float)($this->total_wrapping);
+ $fields['total_wrapping_tax_incl'] = (float)($this->total_wrapping_tax_incl);
+ $fields['total_wrapping_tax_excl'] = (float)($this->total_wrapping_tax_excl);
+ $fields['invoice_number'] = (int)($this->invoice_number);
+ $fields['delivery_number'] = (int)($this->delivery_number);
+ $fields['invoice_date'] = pSQL($this->invoice_date);
+ $fields['delivery_date'] = pSQL($this->delivery_date);
+ $fields['valid'] = (int)($this->valid) ? 1 : 0;
+ $fields['date_add'] = pSQL($this->date_add);
+ $fields['date_upd'] = pSQL($this->date_upd);
+ $fields['reference'] = pSQL($this->reference);
+ $fields['id_warehouse'] = pSQL($this->id_warehouse);
+
+ return $fields;
+ }
+
+ public function __construct($id = NULL, $id_lang = NULL)
+ {
+ parent::__construct($id, $id_lang);
+ if ($this->id_customer)
+ {
+ $customer = new Customer((int)($this->id_customer));
+ $this->_taxCalculationMethod = Group::getPriceDisplayMethod((int)($customer->id_default_group));
+ }
+ else
+ $this->_taxCalculationMethod = Group::getDefaultPriceDisplayMethod();
+ }
+
+ public function getTaxCalculationMethod()
+ {
+ return (int)($this->_taxCalculationMethod);
+ }
+
+ /* Does NOT delete a product but "cancel" it (which means return/refund/delete it depending of the case) */
+ public function deleteProduct($order, $orderDetail, $quantity)
+ {
+ if (!(int)($this->getCurrentState()))
+ return false;
+
+ if ($this->hasBeenDelivered())
+ {
+ if (!Configuration::get('PS_ORDER_RETURN'))
+ die(Tools::displayError());
+ $orderDetail->product_quantity_return += (int)($quantity);
+ return $orderDetail->update();
+ }
+ elseif ($this->hasBeenPaid())
+ {
+ $orderDetail->product_quantity_refunded += (int)($quantity);
+ return $orderDetail->update();
+ }
+ return $this->_deleteProduct($orderDetail, (int)($quantity));
+ }
+
+ /* DOES delete the product */
+ protected function _deleteProduct($orderDetail, $quantity)
+ {
+ $tax_calculator = $orderDetail->getTaxCalculator();
+
+ $price = $tax_calculator->addTaxes($orderDetail->product_price);
+ if ($orderDetail->reduction_percent != 0.00)
+ $reduction_amount = $price * $orderDetail->reduction_percent / 100;
+ elseif ($orderDetail->reduction_amount != '0.000000')
+ $reduction_amount = Tools::ps_round($orderDetail->reduction_amount, 2);
+ if (isset($reduction_amount) AND $reduction_amount)
+ $price = Tools::ps_round($price - $reduction_amount, 2);
+ $productPriceWithoutTax = number_format($tax_calculator->removeTaxes($price), 2, '.', '');
+ $price += Tools::ps_round($orderDetail->ecotax * (1 + $orderDetail->ecotax_tax_rate / 100), 2);
+ $productPrice = number_format($quantity * $price, 2, '.', '');
+ /* Update cart */
+ $cart = new Cart($this->id_cart);
+ $cart->updateQty($quantity, $orderDetail->product_id, $orderDetail->product_attribute_id, false, 0, 'down'); // customization are deleted in deleteCustomization
+ $cart->update();
+
+ /* Update order */
+ $shippingDiff = $this->total_shipping - $cart->getOrderShippingCost();
+ $this->total_products -= $productPriceWithoutTax;
+
+ // After upgrading from old version
+ // total_products_wt is null
+ // removing a product made order total negative
+ // and don't recalculating totals (on getTotalProductsWithTaxes)
+ if ($this->total_products_wt != 0)
+ $this->total_products_wt -= $productPrice;
+
+ $this->total_shipping = $cart->getTotalShippingCost();
+
+ /* It's temporary fix for 1.3 version... */
+ if ($orderDetail->product_quantity_discount != '0.000000')
+ $this->total_paid -= ($productPrice + $shippingDiff);
+ else
+ $this->total_paid = $cart->getOrderTotal();
+
+ $this->total_paid_real -= ($productPrice + $shippingDiff);
+
+ /* Prevent from floating precision issues (total_products has only 2 decimals) */
+ if ($this->total_products < 0)
+ $this->total_products = 0;
+
+ if ($this->total_paid < 0)
+ $this->total_paid = 0;
+
+ if ($this->total_paid_real < 0)
+ $this->total_paid_real = 0;
+
+ /* Prevent from floating precision issues */
+ $this->total_paid = number_format($this->total_paid, 2, '.', '');
+ $this->total_paid_real = number_format($this->total_paid_real, 2, '.', '');
+ $this->total_products = number_format($this->total_products, 2, '.', '');
+ $this->total_products_wt = number_format($this->total_products_wt, 2, '.', '');
+
+ /* Update order detail */
+ $orderDetail->product_quantity -= (int)($quantity);
+
+ if (!$orderDetail->product_quantity)
+ {
+ if (!$orderDetail->delete())
+ return false;
+ if (count($this->getProductsDetail()) == 0)
+ {
+ $history = new OrderHistory();
+ $history->id_order = (int)($this->id);
+ $history->changeIdOrderState(Configuration::get('PS_OS_CANCELED'), (int)($this->id));
+ if (!$history->addWithemail())
+ return false;
+ }
+ return $this->update();
+ }
+ return $orderDetail->update() AND $this->update();
+ }
+
+ public function deleteCustomization($id_customization, $quantity, $orderDetail)
+ {
+ if (!(int)($this->getCurrentState()))
+ return false;
+
+ if ($this->hasBeenDelivered())
+ return Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'customization` SET `quantity_returned` = `quantity_returned` + '.(int)($quantity).' WHERE `id_customization` = '.(int)($id_customization).' AND `id_cart` = '.(int)($this->id_cart).' AND `id_product` = '.(int)($orderDetail->product_id));
+ elseif ($this->hasBeenPaid())
+ return Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'customization` SET `quantity_refunded` = `quantity_refunded` + '.(int)($quantity).' WHERE `id_customization` = '.(int)($id_customization).' AND `id_cart` = '.(int)($this->id_cart).' AND `id_product` = '.(int)($orderDetail->product_id));
+ if (!Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'customization` SET `quantity` = `quantity` - '.(int)($quantity).' WHERE `id_customization` = '.(int)($id_customization).' AND `id_cart` = '.(int)($this->id_cart).' AND `id_product` = '.(int)($orderDetail->product_id)))
+ return false;
+ if (!Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'customization` WHERE `quantity` = 0'))
+ return false;
+ return $this->_deleteProduct($orderDetail, (int)($quantity));
+ }
+
+ /**
+ * Get order history
+ *
+ * @param integer $id_lang Language id
+ *
+ * @return array History entries ordered by date DESC
+ */
+ public function getHistory($id_lang, $id_order_state = false, $no_hidden = false)
+ {
+ if (!$id_order_state)
+ $id_order_state = 0;
+
+ if (!isset(self::$_historyCache[$this->id.'_'.$id_order_state]) OR $no_hidden)
+ {
+ $id_lang = $id_lang ? (int)($id_lang) : 'o.`id_lang`';
+ $result = Db::getInstance()->executeS('
+ SELECT oh.*, e.`firstname` AS employee_firstname, e.`lastname` AS employee_lastname, osl.`name` AS ostate_name
+ FROM `'._DB_PREFIX_.'orders` o
+ LEFT JOIN `'._DB_PREFIX_.'order_history` oh ON o.`id_order` = oh.`id_order`
+ LEFT JOIN `'._DB_PREFIX_.'order_state` os ON os.`id_order_state` = oh.`id_order_state`
+ LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = '.(int)($id_lang).')
+ LEFT JOIN `'._DB_PREFIX_.'employee` e ON e.`id_employee` = oh.`id_employee`
+ WHERE oh.id_order = '.(int)($this->id).'
+ '.($no_hidden ? ' AND os.hidden = 0' : '').'
+ '.((int)($id_order_state) ? ' AND oh.`id_order_state` = '.(int)($id_order_state) : '').'
+ ORDER BY oh.date_add DESC, oh.id_order_history DESC');
+ if ($no_hidden)
+ return $result;
+ self::$_historyCache[$this->id.'_'.$id_order_state] = $result;
+ }
+ return self::$_historyCache[$this->id.'_'.$id_order_state];
+ }
+
+ public function getProductsDetail()
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_detail` od
+ WHERE od.`id_order` = '.(int)($this->id));
+ }
+
+ public function getFirstMessage()
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
+ SELECT `message`
+ FROM `'._DB_PREFIX_.'message`
+ WHERE `id_order` = '.(int)$this->id.'
+ ORDER BY `id_message`
+ ');
+ }
+
+ public function setProductPrices(&$row)
+ {
+ $tax_calculator = OrderDetail::getTaxCalculatorStatic((int)$row['id_order_detail']);
+ $row['tax_calculator'] = $tax_calculator;
+ $row['tax_rate'] = $tax_calculator->getTotalRate();
+
+ if ($this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price'] = Tools::ps_round($row['product_price'], 2);
+ else
+ $row['product_price_wt'] = Tools::ps_round($tax_calculator->addTaxes($row['product_price']), 2);
+
+ $group_reduction = 1;
+ if ($row['group_reduction'] > 0)
+ $group_reduction = 1 - $row['group_reduction'] / 100;
+
+ if ($row['reduction_percent'] != 0)
+ {
+ if ($this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price'] = ($row['product_price'] - $row['product_price'] * ($row['reduction_percent'] * 0.01));
+ else
+ $row['product_price_wt'] = Tools::ps_round(($row['product_price_wt'] - $row['product_price_wt'] * ($row['reduction_percent'] * 0.01)), 2);
+ }
+
+ if ($row['reduction_amount'] != 0)
+ {
+ if ($this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price'] = ($row['product_price'] - ($tax_calculator->removeTaxes($row['reduction_amount'])));
+ else
+ $row['product_price_wt'] = Tools::ps_round(($row['product_price_wt'] - $row['reduction_amount']), 2);
+ }
+
+ if ($row['group_reduction'] > 0)
+ {
+ if ($this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price'] = $row['product_price'] * $group_reduction;
+ else
+ $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] * $group_reduction , 2);
+ }
+
+ if (($row['reduction_percent'] OR $row['reduction_amount'] OR $row['group_reduction']) AND $this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price'] = Tools::ps_round($row['product_price'], 2);
+
+ if ($this->_taxCalculationMethod == PS_TAX_EXC)
+ $row['product_price_wt'] = Tools::ps_round($tax_calculator->addTaxes($row['product_price']), 2) + Tools::ps_round($row['ecotax'] * (1 + $row['ecotax_tax_rate'] / 100), 2);
+ else
+ {
+ $row['product_price_wt_but_ecotax'] = $row['product_price_wt'];
+ $row['product_price_wt'] = Tools::ps_round($row['product_price_wt'] + $row['ecotax'] * (1 + $row['ecotax_tax_rate'] / 100), 2);
+ }
+
+ $row['total_wt'] = $row['product_quantity'] * $row['product_price_wt'];
+ $row['total_price'] = $row['product_quantity'] * $row['product_price'];
+ }
+
+
+ /**
+ * Get order products
+ *
+ * @return array Products with price, quantity (with taxe and without)
+ */
+ public function getProducts($products = false, $selectedProducts = false, $selectedQty = false)
+ {
+ if (!$products)
+ $products = $this->getProductsDetail();
+
+ $customized_datas = Product::getAllCustomizedDatas($this->id_cart);
+
+ $resultArray = array();
+ foreach ($products AS $row)
+ {
+ // Change qty if selected
+ if ($selectedQty)
+ {
+ $row['product_quantity'] = 0;
+ foreach ($selectedProducts AS $key => $id_product)
+ if ($row['id_order_detail'] == $id_product)
+ $row['product_quantity'] = (int)($selectedQty[$key]);
+ if (!$row['product_quantity'])
+ continue ;
+ }
+
+ $this->setProductImageInformations($row);
+ $this->setProductCurrentStock($row);
+ $this->setProductPrices($row);
+ $this->setProductCustomizedDatas($row, $customized_datas);
+
+ // Add information for virtual product
+ if ($row['download_hash'] && !empty($row['download_hash']))
+ {
+ if ($row['product_attribute_id'] && !empty($row['product_attribute_id']))
+ $row['filename'] = ProductDownload::getFilenameFromIdAttribute((int)$row['product_id'], (int)$row['product_attribute_id']);
+ else
+ $row['filename'] = ProductDownload::getFilenameFromIdProduct((int)$row['product_id']);
+ // Get the display filename
+ $row['display_filename'] = ProductDownload::getFilenameFromFilename($row['filename']);
+ }
+ /* Stock product */
+ $resultArray[(int)$row['id_order_detail']] = $row;
+ }
+
+ if ($customized_datas)
+ Product::addCustomizationPrice($resultArray, $customized_datas);
+
+ return $resultArray;
+ }
+
+ protected function setProductCustomizedDatas(&$product, $customized_datas)
+ {
+ $product['customizedDatas'] = null;
+ if (isset($customized_datas[$product['product_id']][$product['product_attribute_id']]))
+ $product['customizedDatas'] = $customized_datas[$product['product_id']][$product['product_attribute_id']];
+ else
+ $product['customizationQuantityTotal'] = 0;
+ }
+
+ /**
+ *
+ * This method allow to add stock information on a product detail
+ * @param array &$product
+ */
+ protected function setProductCurrentStock(&$product)
+ {
+ $product['current_stock'] = StockManagerFactory::getManager()->getProductPhysicalQuantities($product['product_id'], $product['product_attribute_id'], null, true);
+ }
+
+ /**
+ *
+ * This method allow to add image information on a product detail
+ * @param array &$product
+ */
+ protected function setProductImageInformations(&$product)
+ {
+ if (isset($product['product_attribute_id']) && $product['product_attribute_id'])
+ $id_image = Db::getInstance()->getValue('
+ SELECT id_image
+ FROM '._DB_PREFIX_.'product_attribute_image
+ WHERE id_product_attribute = '.(int)$product['product_attribute_id']);
+
+ if (!isset($image['id_image']) || !$image['id_image'])
+ $id_image = Db::getInstance()->getValue('
+ SELECT id_image
+ FROM '._DB_PREFIX_.'image
+ WHERE id_product = '.(int)($product['product_id']).' AND cover = 1
+ ');
+
+ $product['image'] = null;
+ $product['image_size'] = null;
+
+ if ($id_image)
+ $product['image'] = new Image($id_image);
+ }
+
+ public function getTaxesAverageUsed()
+ {
+ return Cart::getTaxesAverageUsed((int)($this->id_cart));
+ }
+
+ /**
+ * Count virtual products in order
+ *
+ * @return int number of virtual products
+ */
+ public function getVirtualProducts()
+ {
+ $sql = '
+ SELECT `product_id`, `product_attribute_id`, `download_hash`, `download_deadline`
+ FROM `'._DB_PREFIX_.'order_detail` od
+ WHERE od.`id_order` = '.(int)($this->id).'
+ AND `download_hash` <> \'\'';
+ return Db::getInstance()->executeS($sql);
+ }
+
+ /**
+ * Check if order contains (only) virtual products
+ *
+ * @param boolean $strict If false return true if there are at least one product virtual
+ * @return boolean true if is a virtual order or false
+ *
+ */
+ public function isVirtual($strict = true)
+ {
+ $products = $this->getProducts();
+ if (count($products) < 1)
+ return false;
+ $virtual = true;
+ foreach ($products AS $product)
+ {
+ $pd = ProductDownload::getIdFromIdProduct((int)($product['product_id']));
+ if ($pd AND Validate::isUnsignedInt($pd) AND $product['download_hash'] AND $product['display_filename'] != '')
+ {
+ if ($strict === false)
+ return true;
+ }
+ else
+ $virtual &= false;
+ }
+ return $virtual;
+ }
+
+ /**
+ * @deprecated 1.5.0.1
+ */
+ public function getDiscounts($details = false)
+ {
+ Tools::displayAsDeprecated();
+ return Order::getCartRules();
+ }
+
+ public function getCartRules()
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_cart_rule` ocr
+ LEFT JOIN `'._DB_PREFIX_.'cart_rule` cr ON cr.`id_cart_rule` = ocr.`id_cart_rule`
+ WHERE ocr.`id_order` = '.(int)$this->id);
+ }
+
+ public static function getDiscountsCustomer($id_customer, $id_cart_rule)
+ {
+ return Db::getInstance()->getValue('
+ SELECT COUNT(*) FROM `'._DB_PREFIX_.'orders` o
+ LEFT JOIN '._DB_PREFIX_.'order_cart_rule ocr ON (ocr.id_order = o.id_order)
+ WHERE o.id_customer = '.(int)$id_customer.'
+ AND ocr.id_cart_rule = '.(int)$id_cart_rule);
+ }
+
+ /**
+ * Get current order state (eg. Awaiting payment, Delivered...)
+ *
+ * @return array Order state details
+ */
+ public function getCurrentState()
+ {
+ $orderHistory = OrderHistory::getLastOrderState($this->id);
+ if (!isset($orderHistory) OR !$orderHistory)
+ return false;
+ return $orderHistory->id;
+ }
+
+ /**
+ * Get current order state name (eg. Awaiting payment, Delivered...)
+ *
+ * @return array Order state details
+ */
+ public function getCurrentStateFull($id_lang)
+ {
+ return Db::getInstance()->getRow('
+ SELECT oh.`id_order_state`, osl.`name`, os.`logable`, os.`shipped`
+ FROM `'._DB_PREFIX_.'order_history` oh
+ LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (osl.`id_order_state` = oh.`id_order_state`)
+ LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (os.`id_order_state` = oh.`id_order_state`)
+ WHERE osl.`id_lang` = '.(int)($id_lang).' AND oh.`id_order` = '.(int)($this->id).'
+ ORDER BY `date_add` DESC, `id_order_history` DESC');
+ }
+
+ public function hasBeenDelivered()
+ {
+ return sizeof($this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_DELIVERED')));
+ }
+
+ public function hasBeenPaid()
+ {
+ return sizeof($this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_PAYMENT')));
+ }
+
+ public function hasBeenShipped()
+ {
+ return sizeof($this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_SHIPPING')));
+ }
+
+ public function isInPreparation()
+ {
+ return sizeof($this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_PREPARATION')));
+ }
+
+ /**
+ * Get customer orders
+ *
+ * @param integer $id_customer Customer id
+ * @param boolean $showHiddenStatus Display or not hidden order statuses
+ * @return array Customer orders
+ */
+ static public function getCustomerOrders($id_customer, $showHiddenStatus = false, Context $context = null)
+ {
+ if (!$context)
+ $context = Context::getContext();
+
+ $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT o.*, (SELECT SUM(od.`product_quantity`) FROM `'._DB_PREFIX_.'order_detail` od WHERE od.`id_order` = o.`id_order`) nb_products
+ FROM `'._DB_PREFIX_.'orders` o
+ WHERE o.`id_customer` = '.(int)$id_customer.'
+ GROUP BY o.`id_order`
+ ORDER BY o.`date_add` DESC');
+ if (!$res)
+ return array();
+
+ foreach ($res AS $key => $val)
+ {
+ $res2 = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT os.`id_order_state`, osl.`name` AS order_state, os.`invoice`
+ FROM `'._DB_PREFIX_.'order_history` oh
+ LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (os.`id_order_state` = oh.`id_order_state`)
+ INNER JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = '.(int)$context->language->id.')
+ WHERE oh.`id_order` = '.(int)($val['id_order']).(!$showHiddenStatus ? ' AND os.`hidden` != 1' : '').'
+ ORDER BY oh.`date_add` DESC, oh.`id_order_history` DESC
+ LIMIT 1');
+
+ if ($res2)
+ $res[$key] = array_merge($res[$key], $res2[0]);
+
+ }
+ return $res;
+ }
+
+ public static function getOrdersIdByDate($date_from, $date_to, $id_customer = NULL, $type = NULL)
+ {
+ $sql = 'SELECT `id_order`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE DATE_ADD(date_upd, INTERVAL -1 DAY) <= \''.pSQL($date_to).'\' AND date_upd >= \''.pSQL($date_from).'\'
+ '.Context::getContext()->shop->addSqlRestriction()
+ .($type ? ' AND '.pSQL(strval($type)).'_number != 0' : '')
+ .($id_customer ? ' AND id_customer = '.(int)($id_customer) : '');
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
+
+ $orders = array();
+ foreach ($result AS $order)
+ $orders[] = (int)($order['id_order']);
+ return $orders;
+ }
+
+ static public function getOrdersWithInformations($limit = NULL, Context $context = null)
+ {
+ if (!$context)
+ $context = Context::getContext();
+
+ $sql = 'SELECT *, (
+ SELECT `name`
+ FROM `'._DB_PREFIX_.'order_history` oh
+ LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (osl.`id_order_state` = oh.`id_order_state`)
+ WHERE oh.`id_order` = o.`id_order`
+ AND osl.`id_lang` = '.(int)$context->language->id.'
+ ORDER BY oh.`date_add` DESC
+ LIMIT 1
+ ) AS `state_name`
+ FROM `'._DB_PREFIX_.'orders` o
+ LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = o.`id_customer`)
+ WHERE 1
+ '.Context::getContext()->shop->addSqlRestriction(false, 'o').'
+ ORDER BY o.`date_add` DESC
+ '.((int)$limit ? 'LIMIT 0, '.(int)$limit : '');
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
+ }
+
+ public static function getOrdersIdInvoiceByDate($date_from, $date_to, $id_customer = NULL, $type = NULL)
+ {
+ $sql = 'SELECT `id_order`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE DATE_ADD(invoice_date, INTERVAL -1 DAY) <= \''.pSQL($date_to).'\' AND invoice_date >= \''.pSQL($date_from).'\'
+ '.Context::getContext()->shop->addSqlRestriction()
+ .($type ? ' AND '.pSQL(strval($type)).'_number != 0' : '')
+ .($id_customer ? ' AND id_customer = '.(int)($id_customer) : '').
+ ' ORDER BY invoice_date ASC';
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
+
+ $orders = array();
+ foreach ($result AS $order)
+ $orders[] = (int)($order['id_order']);
+ return $orders;
+ }
+
+ public static function getOrderIdsByStatus($id_order_state)
+ {
+ $sql = 'SELECT id_order
+ FROM '._DB_PREFIX_.'orders o
+ WHERE '.(int)$id_order_state.' = (
+ SELECT id_order_state
+ FROM '._DB_PREFIX_.'order_history oh
+ WHERE oh.id_order = o.id_order
+ ORDER BY date_add DESC, id_order_history DESC
+ LIMIT 1
+ )
+ '.Context::getContext()->shop->addSqlRestriction(false, 'o').'
+ ORDER BY invoice_date ASC';
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
+
+ $orders = array();
+ foreach ($result AS $order)
+ $orders[] = (int)($order['id_order']);
+ return $orders;
+ }
+
+ /**
+ * Get product total without taxes
+ *
+ * @return Product total with taxes
+ */
+ public function getTotalProductsWithoutTaxes($products = false)
+ {
+ return $this->total_products;
+ }
+
+ /**
+ * Get product total with taxes
+ *
+ * @return Product total with taxes
+ */
+ public function getTotalProductsWithTaxes($products = false)
+ {
+ if ($this->total_products_wt != '0.00' AND !$products)
+ return $this->total_products_wt;
+ /* Retro-compatibility (now set directly on the validateOrder() method) */
+
+ if (!$products)
+ $products = $this->getProductsDetail();
+
+ $return = 0;
+
+ foreach ($products AS $row)
+ {
+ if (!isset($row['tax_rate']))
+ $row['tax_rate'] = 0;
+
+ $price = Tools::ps_round($row['product_price'] * (1 + $row['tax_rate'] / 100), 2);
+ if ($row['reduction_percent'])
+ $price -= $price * ($row['reduction_percent'] * 0.01);
+ if ($row['reduction_amount'])
+ $price -= $row['reduction_amount'] * (1 + ($row['tax_rate'] * 0.01));
+ if ($row['group_reduction'])
+ $price -= $price * ($row['group_reduction'] * 0.01);
+ $price += $row['ecotax'] * (1 + $row['ecotax_tax_rate'] / 100);
+ $return += Tools::ps_round($price, 2) * $row['product_quantity'];
+ }
+ if (!$products)
+ {
+ $this->total_products_wt = $return;
+ $this->update();
+ }
+ return $return;
+ }
+
+ /**
+ * Get customer orders number
+ *
+ * @param integer $id_customer Customer id
+ * @return array Customer orders number
+ */
+ public static function getCustomerNbOrders($id_customer)
+ {
+ $sql = 'SELECT COUNT(`id_order`) AS nb
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_customer` = '.(int)$id_customer
+ .Context::getContext()->shop->addSqlRestriction();
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
+
+ return isset($result['nb']) ? $result['nb'] : 0;
+ }
+
+ /**
+ * Get an order by its cart id
+ *
+ * @param integer $id_cart Cart id
+ * @return array Order details
+ */
+ public static function getOrderByCartId($id_cart)
+ {
+ $sql = 'SELECT `id_order`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_cart` = '.(int)($id_cart)
+ .Context::getContext()->shop->addSqlRestriction();
+ $result = Db::getInstance()->getRow($sql);
+
+ return isset($result['id_order']) ? $result['id_order'] : false;
+ }
+
+ /**
+ * @deprecated 1.5.0.1
+ */
+ public function addDiscount($id_cart_rule, $name, $value)
+ {
+ Tools::displayAsDeprecated();
+ return Order::addCartRule($id_cart_rule, $name, $value);
+ }
+
+ public function addCartRule($id_cart_rule, $name, $value)
+ {
+ return Db::getInstance()->AutoExecute(_DB_PREFIX_.'order_cart_rule', array('id_order' => (int)$this->id, 'id_cart_rule' => (int)$id_cart_rule, 'name' => pSQL($name), 'value' => (float)$value), 'INSERT');
+ }
+
+ public function getNumberOfDays()
+ {
+ $nbReturnDays = (int)(Configuration::get('PS_ORDER_RETURN_NB_DAYS'));
+ if (!$nbReturnDays)
+ return true;
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
+ SELECT TO_DAYS(NOW()) - TO_DAYS(`delivery_date`) AS days FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_order` = '.(int)($this->id));
+ if ($result['days'] <= $nbReturnDays)
+ return true;
+ return false;
+ }
+
+
+ public function isReturnable()
+ {
+ $payment = $this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_PAYMENT'));
+ $delivred = $this->getHistory((int)($this->id_lang), Configuration::get('PS_OS_DELIVERED'));
+ if ($payment AND $delivred AND strtotime($delivred[0]['date_add']) < strtotime($payment[0]['date_add']))
+ return ((int)(Configuration::get('PS_ORDER_RETURN')) == 1 AND $this->getNumberOfDays());
+ else
+ return ((int)(Configuration::get('PS_ORDER_RETURN')) == 1 AND (int)($this->getCurrentState()) == Configuration::get('PS_OS_DELIVERED') AND $this->getNumberOfDays());
+ }
+
+
+ public static function getLastInvoiceNumber()
+ {
+ return (int)Db::getInstance()->getValue('
+ SELECT MAX(`invoice_number`) AS `invoice_number`
+ FROM `'._DB_PREFIX_.'orders`');
+ }
+
+ public function setInvoice()
+ {
+ $number = (int)Configuration::get('PS_INVOICE_START_NUMBER');
+ if ($number)
+ Configuration::updateValue('PS_INVOICE_START_NUMBER', false);
+ else
+ $number = '(SELECT `invoice_number`
+ FROM (
+ SELECT MAX(`invoice_number`) + 1 AS `invoice_number`
+ FROM `'._DB_PREFIX_.'orders`)
+ tmp )';
+ // a way to avoid duplicate invoice number
+ Db::getInstance()->execute('
+ UPDATE `'._DB_PREFIX_.'orders`
+ SET `invoice_number` = '.$number.', `invoice_date` = \''.date('Y-m-d H:i:s').'\'
+ WHERE `id_order` = '.(int)$this->id
+ );
+ $res = Db::getInstance()->getRow('
+ SELECT `invoice_number`, `invoice_date`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_order` = '.(int)$this->id
+ );
+
+ $this->invoice_date = $res['invoice_date'];
+ $this->invoice_number = $res['invoice_number'];
+ }
+
+ public function setDelivery()
+ {
+ // Set delivery number
+ $number = (int)(Configuration::get('PS_DELIVERY_NUMBER'));
+ if (!(int)($number))
+ die(Tools::displayError('Invalid delivery number'));
+ $this->delivery_number = $number;
+ Configuration::updateValue('PS_DELIVERY_NUMBER', $number + 1);
+
+ // Set delivery date
+ $this->delivery_date = date('Y-m-d H:i:s');
+
+ // Update object
+ $this->update();
+ }
+
+ public static function printPDFIcons($id_order, $tr)
+ {
+ $order = new Order($id_order);
+ $orderState = OrderHistory::getLastOrderState($id_order);
+ if (!Validate::isLoadedObject($orderState) OR !Validate::isLoadedObject($order))
+ die(Tools::displayError('Invalid objects'));
+ echo '';
+ if (($orderState->invoice AND $order->invoice_number) AND (int)($tr['product_number']))
+ echo '
';
+ else
+ echo ' ';
+ echo '';
+ echo '';
+ if ($orderState->delivery AND $order->delivery_number)
+ echo '
';
+ else
+ echo ' ';
+ echo '';
+ }
+
+ public static function getByDelivery($id_delivery)
+ {
+ $sql = 'SELECT id_order
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `delivery_number` = '.(int)($id_delivery).'
+ '.Context::getContext()->shop->addSqlRestriction();
+ $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
+ return new Order((int)($res['id_order']));
+ }
+
+ public function getTotalWeight()
+ {
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
+ SELECT SUM(product_weight * product_quantity) weight
+ FROM '._DB_PREFIX_.'order_detail
+ WHERE id_order = '.(int)($this->id));
+
+ return (float)($result['weight']);
+ }
+
+ public static function getInvoice($id_invoice)
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
+ SELECT `invoice_number`, `id_order`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE invoice_number = '.(int)($id_invoice));
+ }
+
+ public function isAssociatedAtGuest($email)
+ {
+ if (!$email)
+ return false;
+ $sql = 'SELECT COUNT(*)
+ FROM `'._DB_PREFIX_.'orders` o
+ LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = o.`id_customer`)
+ WHERE o.`id_order` = '.(int)$this->id.'
+ AND c.`email` = \''.pSQL($email).'\'
+ AND c.`is_guest` = 1
+ '.Context::getContext()->shop->addSqlRestriction(false, 'c');
+ return (bool)Db::getInstance()->getValue($sql);
+ }
+
+ /**
+ * @param int $id_order
+ * @param int $id_customer optionnal
+ * @return int id_cart
+ */
+ public static function getCartIdStatic($id_order, $id_customer = 0)
+ {
+ return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
+ SELECT `id_cart`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_order` = '.(int)$id_order.'
+ '.($id_customer ? 'AND `id_customer` = '.(int)$id_customer : ''));
+ }
+
+ public function getWsOrderRows()
+ {
+ $query = 'SELECT id_order_detail as `id`, `product_id`, `product_price`, `id_order`, `product_attribute_id`, `product_quantity`, `product_name`
+ FROM `'._DB_PREFIX_.'order_detail`
+ WHERE id_order = '.(int)$this->id;
+ $result = Db::getInstance()->executeS($query);
+ return $result;
+ }
+
+ public function setCurrentState($id_order_state, $id_employee)
+ {
+ if (empty($id_order_state))
+ return false;
+ $history = new OrderHistory();
+ $history->id_order = (int)$this->id;
+ $history->id_employee = (int)$id_employee;
+ $history->changeIdOrderState((int)$id_order_state, (int)$this->id);
+ $res = Db::getInstance()->getRow('
+ SELECT `invoice_number`, `invoice_date`, `delivery_number`, `delivery_date`
+ FROM `'._DB_PREFIX_.'orders`
+ WHERE `id_order` = '.(int)$this->id);
+ $this->invoice_date = $res['invoice_date'];
+ $this->invoice_number = $res['invoice_number'];
+ $this->delivery_date = $res['delivery_date'];
+ $this->delivery_number = $res['delivery_number'];
+ $history->addWithemail();
+ }
+
+ public function addWs($autodate = true, $nullValues = false)
+ {
+ $paymentModule = Module::getInstanceByName($this->module);
+ $customer = new Customer($this->id_customer);
+ $paymentModule->validateOrder($this->id_cart, Configuration::get('PS_OS_WS_PAYMENT'), $this->total_paid, $this->payment, NULL, array(), null, false, $customer->secure_key);
+ $this->id = $paymentModule->currentOrder;
+ return true;
+ }
+
+ public function deleteAssociations()
+ {
+ return (Db::getInstance()->execute('
+ DELETE FROM `'._DB_PREFIX_.'order_detail`
+ WHERE `id_order` = '.(int)($this->id)) !== false);
+ }
+
+ /**
+ * This method return the ID of the previous order
+ * @return int
+ */
+ public function getPreviousOrderId()
+ {
+ return Db::getInstance()->getValue('
+ SELECT id_order
+ FROM '._DB_PREFIX_.'orders
+ WHERE id_order < '.(int)$this->id.'
+ ORDER BY id_order DESC');
+ }
+
+ /**
+ * This method return the ID of the next order
+ * @return int
+ */
+ public function getNextOrderId()
+ {
+ return Db::getInstance()->getValue('
+ SELECT id_order
+ FROM '._DB_PREFIX_.'orders
+ WHERE id_order > '.(int)$this->id.'
+ ORDER BY id_order ASC');
+ }
+
+ /**
+ * Get the an order detail list of the current order
+ * @return array
+ */
+ public function getOrderDetailList()
+ {
+ return OrderDetail::getList($this->id);
+ }
+
+ /**
+ * Gennerate a unique reference for orders generated with the same cart id
+ * This references, is usefull for check payment
+ *
+ * @return String
+ */
+ public static function generateReference()
+ {
+ // To generate a random reference, we first generate a random number
+ // This number is a rand concated with the current timestamp
+ $rand = (int)(microtime(true) * 100 % 10000000000).rand(10, 1000);
+ $reference = '';
+
+ do {
+ $reference .= chr(65 + $rand % 26);
+ $rand = (int)($rand / 26);
+ } while ($rand > 26);
+
+ // Check if semi-random string generated is not already used
+ // /!\ Here we CANNOT/MUSTN'T use _PS_USE_SQL_SLAVE_
+ if (Db::getInstance()->getValue('
+ SELECT count(*)
+ FROM '._DB_PREFIX_.'orders
+ WHERE reference = \''.$reference.'\'') > 0)
+ return self::generateReference(); // If the reference already exists, generate a new one
+
+ return $reference;
+ }
+
+ public function orderContainProduct($id_product)
+ {
+ $product_list = $this->getOrderDetailList();
+ foreach ($product_list as $product)
+ if ($product['product_id'] == (int)$id_product)
+ return true;
+ return false;
+ }
+ /**
+ * This method returns true if at least one order details uses the
+ * One After Another tax computation method.
+ *
+ * @since 1.5.0.1
+ * @return boolean
+ */
+ public function useOneAfterAnotherTaxComputationMethod()
+ {
+ // if one of the order details use the tax computation method the display will be different
+ return Db::getInstance()->getValue('
+ SELECT od.`tax_computation_method`
+ FROM `'._DB_PREFIX_.'order_detail_tax` odt
+ LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON (od.`id_order_detail` = odt.`id_order_detail`)
+ WHERE od.`id_order` = '.(int)$this->id.'
+ AND od.`tax_computation_method` = '.(int)TaxCalculator::ONE_AFTER_ANOTHER_METHOD
+ );
+
+ }
+
+
+ /**
+ * Returns the correct product taxes breakdown.
+ *
+ * @since 1.5.0.1
+ * @return array
+ */
+ public function getProductTaxesBreakdown()
+ {
+ $tmp_tax_infos = array();
+ if ($this->useOneAfterAnotherTaxComputationMethod())
+ {
+ // sum by taxes
+ $taxes_by_tax = Db::getInstance()->executeS('
+ SELECT odt.`id_order_detail`, t.`name`, t.`rate`, SUM(`total_amount`) AS `total_amount`
+ FROM `'._DB_PREFIX_.'order_detail_tax` odt
+ LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = odt.`id_tax`)
+ LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON (od.`id_order_detail` = odt.`id_order_detail`)
+ WHERE od.`id_order` = '.(int)$this->id.'
+ GROUP BY odt.`id_tax`
+ ');
+
+ // format response
+ $tmp_tax_infos = array();
+ foreach ($taxes_infos as $tax_infos)
+ {
+ $tmp_tax_infos[$tax_infos['rate']]['total_amount'] = $tax_infos['tax_amount'];
+ $tmp_tax_infos[$tax_infos['rate']]['name'] = $tax_infos['name'];
+ }
+ }
+ else
+ {
+ // sum by order details in order to retrieve real taxes rate
+ $taxes_infos = Db::getInstance()->executeS('
+ SELECT odt.`id_order_detail`, t.`rate` AS `name`, SUM(od.`total_price_tax_excl`) AS total_price_tax_excl, SUM(t.`rate`) AS rate, SUM(`total_amount`) AS `total_amount`
+ FROM `'._DB_PREFIX_.'order_detail_tax` odt
+ LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = odt.`id_tax`)
+ LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON (od.`id_order_detail` = odt.`id_order_detail`)
+ WHERE od.`id_order` = '.(int)$this->id.'
+ GROUP BY odt.`id_order_detail`
+ ');
+
+ // sum by taxes
+ $tmp_tax_infos = array();
+ foreach ($taxes_infos as $tax_infos)
+ {
+ if (!isset($tmp_tax_infos[$tax_infos['rate']]))
+ $tmp_tax_infos[$tax_infos['rate']] = array('total_amount' => 0,
+ 'name' => 0,
+ 'total_price_tax_excl' => 0);
+
+ $tmp_tax_infos[$tax_infos['rate']]['total_amount'] += $tax_infos['total_amount'];
+ $tmp_tax_infos[$tax_infos['rate']]['name'] = $tax_infos['name'];
+ $tmp_tax_infos[$tax_infos['rate']]['total_price_tax_excl'] += $tax_infos['total_price_tax_excl'];
+ }
+ }
+
+ return $tmp_tax_infos;
+ }
+
+ /**
+ * Returns the shipping taxes breakdown
+ *
+ * @since 1.5.0.1
+ * @return array
+ */
+ public function getShippingTaxesBreakdown()
+ {
+ $taxes_breakdown = array();
+
+ $shipping_tax_amount = $this->total_shipping_tax_incl - $this->total_shipping_tax_excl;
+
+ if ($shipping_tax_amount > 0)
+ $taxes_breakdown[] = array(
+ 'rate' => $this->carrier_tax_rate,
+ 'total_amount' => $shipping_tax_amount
+ );
+
+ return $taxes_breakdown;
+ }
+
+ /**
+ * Returns the wrapping taxes breakdown
+ * @todo
+
+ * @since 1.5.0.1
+ * @return array
+ */
+ public function getWrappingTaxesBreakdown()
+ {
+ $taxes_breakdown = array();
+ return $taxes_breakdown;
+ }
+
+ /**
+ * Returns the ecotax taxes breakdown
+ *
+ * @since 1.5.0.1
+ * @return array
+ */
+ public function getEcoTaxTaxesBreakdown()
+ {
+ return Db::getInstance()->executeS('
+ SELECT `ecotax_tax_rate`, SUM(`ecotax`) as `ecotax_tax_excl`, SUM(`ecotax`) as `ecotax_tax_incl`
+ FROM `'._DB_PREFIX_.'order_detail`
+ WHERE `id_order` = '.(int)$this->id
+ );
+ }
+}
+
diff --git a/classes/order/OrderCartRule.php b/classes/order/OrderCartRule.php
new file mode 100644
index 000000000..629751ef8
--- /dev/null
+++ b/classes/order/OrderCartRule.php
@@ -0,0 +1,71 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderCartRuleCore extends ObjectModel
+{
+ /** @var integer */
+ public $id_order_cart_rule;
+
+ /** @var integer */
+ public $id_order;
+
+ /** @var integer */
+ public $id_cart_rule;
+
+ /** @var string */
+ public $name;
+
+ /** @var integer */
+ public $value;
+
+ protected $tables = array ('order_cart_rule');
+
+ protected $fieldsRequired = array ('id_order', 'name', 'value');
+ protected $fieldsValidate = array ('id_order' => 'isUnsignedId', 'name' => 'isGenericName', 'value' => 'isInt');
+
+ /* MySQL does not allow 'order detail' for a table name */
+ protected $table = 'order_cart_rule';
+ protected $identifier = 'id_order_cart_rule';
+
+ protected $webserviceParameters = array(
+ 'fields' => array(
+ 'id_order' => array('xlink_resource' => 'orders'),
+ ),
+ );
+
+ public function getFields()
+ {
+ $this->validateFields();
+
+ $fields['id_order'] = (int)($this->id_order);
+ $fields['name'] = pSQL($this->name);
+ $fields['value'] = (int)($this->value);
+
+ return $fields;
+ }
+}
+
diff --git a/classes/order/OrderDetail.php b/classes/order/OrderDetail.php
new file mode 100644
index 000000000..0c0e0bb53
--- /dev/null
+++ b/classes/order/OrderDetail.php
@@ -0,0 +1,555 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderDetailCore extends ObjectModel
+{
+ /** @var integer */
+ public $id_order_detail;
+
+ /** @var integer */
+ public $id_order;
+
+ /** @var integer */
+ public $product_id;
+
+ /** @var integer */
+ public $product_attribute_id;
+
+ /** @var string */
+ public $product_name;
+
+ /** @var integer */
+ public $product_quantity;
+
+ /** @var integer */
+ public $product_quantity_in_stock;
+
+ /** @var integer */
+ public $product_quantity_return;
+
+ /** @var integer */
+ public $product_quantity_refunded;
+
+ /** @var integer */
+ public $product_quantity_reinjected;
+
+ /** @var float */
+ public $product_price;
+
+ /** @var float */
+ public $unit_price_tax_incl;
+
+ /** @var float */
+ public $unit_price_tax_excl;
+
+ /** @var float */
+ public $total_price_tax_incl;
+
+ /** @var float */
+ public $total_price_tax_excl;
+
+ /** @var float */
+ public $reduction_percent;
+
+ /** @var float */
+ public $reduction_amount;
+
+ /** @var float */
+ public $group_reduction;
+
+ /** @var float */
+ public $product_quantity_discount;
+
+ /** @var string */
+ public $product_ean13;
+
+ /** @var string */
+ public $product_upc;
+
+ /** @var string */
+ public $product_reference;
+
+ /** @var string */
+ public $product_supplier_reference;
+
+ /** @var float */
+ public $product_weight;
+
+ /** @var float */
+ public $ecotax;
+
+ /** @var float */
+ public $ecotax_tax_rate;
+
+ /** @var integer */
+ public $discount_quantity_applied;
+
+ /** @var string */
+ public $download_hash;
+
+ /** @var integer */
+ public $download_nb;
+
+ /** @var date */
+ public $download_deadline;
+
+ /** @var string $tax_name **/
+ public $tax_name;
+
+ /** @var float $tax_rate **/
+ public $tax_rate;
+
+ protected $tables = array('order_detail');
+
+ protected $fieldsRequired = array(
+ 'id_order',
+ 'product_name',
+ 'product_quantity',
+ 'product_price');
+
+ protected $fieldsValidate = array(
+ 'id_order' => 'isUnsignedId',
+ 'product_id' => 'isUnsignedId',
+ 'product_attribute_id' => 'isUnsignedId',
+ 'product_name' => 'isGenericName',
+ 'product_quantity' => 'isInt',
+ 'product_quantity_in_stock' => 'isInt',
+ 'product_quantity_return' => 'isUnsignedInt',
+ 'product_quantity_refunded' => 'isUnsignedInt',
+ 'product_quantity_reinjected' => 'isUnsignedInt',
+ 'product_price' => 'isPrice',
+ 'reduction_percent' => 'isFloat',
+ 'reduction_amount' => 'isPrice',
+ 'group_reduction' => 'isFloat',
+ 'product_quantity_discount' => 'isFloat',
+ 'product_ean13' => 'isEan13',
+ 'product_upc' => 'isUpc',
+ 'product_reference' => 'isReference',
+ 'product_supplier_reference' => 'isReference',
+ 'product_weight' => 'isFloat',
+ 'tax_name' => 'isGenericName',
+ 'tax_rate' => 'isFloat',
+ 'ecotax' => 'isFloat',
+ 'ecotax_tax_rate' => 'isFloat',
+ 'discount_quantity_applied' => 'isInt',
+ 'download_hash' => 'isGenericName',
+ 'download_nb' => 'isInt',
+ 'download_deadline' => 'isDateFormat',
+ 'unit_price_tax_incl' => 'isPrice',
+ 'unit_price_tax_excl' => 'isPrice',
+ 'total_price_tax_incl' => 'isPrice',
+ 'total_price_tax_excl' => 'isPrice'
+ );
+
+ protected $table = 'order_detail';
+ protected $identifier = 'id_order_detail';
+
+ protected $webserviceParameters = array(
+ 'fields' => array (
+ 'id_order' => array('xlink_resource' => 'orders'),
+ 'product_id' => array('xlink_resource' => 'products'),
+ 'product_attribute_id' => array('xlink_resource' => 'combinations'),
+ 'product_quantity_reinjected' => array(),
+ 'group_reduction' => array(),
+ 'discount_quantity_applied' => array(),
+ 'download_hash' => array(),
+ 'download_deadline' => array()
+ )
+ );
+
+ /** @var bool */
+ protected $outOfStock = false;
+
+ /** @var TaxCalculator object */
+ protected $tax_calculator = null;
+
+ /** @var Address object */
+ protected $vat_address = null;
+
+ /** @var Address object */
+ protected $specificPrice = null;
+
+ /** @var Customer object */
+ protected $customer = null;
+
+ /** @var Context object */
+ protected $context = null;
+
+ public function __construct($id = null, $id_lang = null, $context = null)
+ {
+ $this->context = $context;
+ $id_shop = null;
+ if ($this->context != null && isset($this->context->shop))
+ $id_shop = $this->context->shop->getID();
+ return parent::__construct($id, $id_lang, $id_shop);
+ }
+
+ public function getFields()
+ {
+ $this->validateFields();
+
+ $fields['id_order'] = (int)$this->id_order;
+ $fields['product_id'] = (int)$this->product_id;
+ $fields['product_attribute_id'] = (int)$this->product_attribute_id;
+ $fields['product_name'] = pSQL($this->product_name);
+ $fields['product_quantity'] = (int)$this->product_quantity;
+ $fields['product_quantity_in_stock'] = (int)$this->product_quantity_in_stock;
+ $fields['product_quantity_return'] = (int)$this->product_quantity_return;
+ $fields['product_quantity_refunded'] = (int)$this->product_quantity_refunded;
+ $fields['product_quantity_reinjected'] = (int)$this->product_quantity_reinjected;
+ $fields['product_price'] = (float)$this->product_price;
+ $fields['reduction_percent'] = (float)$this->reduction_percent;
+ $fields['reduction_amount'] = (float)$this->reduction_amount;
+ $fields['group_reduction'] = (float)$this->group_reduction;
+ $fields['product_quantity_discount'] = (float)$this->product_quantity_discount;
+ $fields['product_ean13'] = pSQL($this->product_ean13);
+ $fields['product_upc'] = pSQL($this->product_upc);
+ $fields['product_reference'] = pSQL($this->product_reference);
+ $fields['product_supplier_reference'] = pSQL($this->product_reference);
+ $fields['product_weight'] = (float)$this->product_weight;
+ $fields['tax_name'] = pSQL($this->tax_name);
+ $fields['tax_rate'] = (float)$this->tax_rate;
+ $fields['ecotax'] = (float)$this->ecotax;
+ $fields['ecotax_tax_rate'] = (float)$this->ecotax_tax_rate;
+ $fields['download_hash'] = pSQL($this->download_hash);
+ $fields['download_nb'] = (int)$this->download_nb;
+ $fields['download_deadline'] = pSQL($this->download_deadline);
+ $fields['unit_price_tax_incl'] = (float)$this->unit_price_tax_incl;
+ $fields['unit_price_tax_excl'] = (float)$this->unit_price_tax_excl;
+ $fields['total_price_tax_incl'] = (float)$this->total_price_tax_incl;
+ $fields['total_price_tax_excl'] = (float)$this->total_price_tax_excl;
+
+ return $fields;
+ }
+
+ public static function getDownloadFromHash($hash)
+ {
+ if ($hash == '') return false;
+ $sql = 'SELECT *
+ FROM `'._DB_PREFIX_.'order_detail` od
+ LEFT JOIN `'._DB_PREFIX_.'product_download` pd ON (od.`product_id`=pd.`id_product`)
+ WHERE od.`download_hash` = \''.pSQL(strval($hash)).'\'
+ AND od.`product_attribute_id` = pd.`id_product_attribute`
+ AND pd.`active` = 1';
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
+ }
+
+ public static function incrementDownload($id_order_detail, $increment = 1)
+ {
+ $sql = 'UPDATE `'._DB_PREFIX_.'order_detail`
+ SET `download_nb` = `download_nb` + '.(int)$increment.'
+ WHERE `id_order_detail`= '.(int)$id_order_detail.'
+ LIMIT 1';
+ return Db::getInstance()->execute($sql);
+ }
+
+ /**
+ * Returns the tax calculator associated to this order detail.
+ * @since 1.5.0.1
+ * @return TaxCalculator
+ */
+ public function getTaxCalculator()
+ {
+ return OrderDetail::getTaxCalculatorStatic($this->id);
+ }
+
+ /**
+ * Return the tax calculator associated to this order_detail
+ * @since 1.5.0.1
+ * @param int $id_order_detail
+ * @return TaxCalculator
+ */
+ public static function getTaxCalculatorStatic($id_order_detail)
+ {
+ $sql = 'SELECT t.*, d.`tax_computation_method`
+ FROM `'._DB_PREFIX_.'order_detail_tax` t
+ LEFT JOIN `'._DB_PREFIX_.'order_detail` d ON (d.`id_order_detail` = t.`id_order_detail`)
+ WHERE d.`id_order_detail` = '.(int)$id_order_detail;
+
+ $computation_method = 1;
+ $taxes = array();
+ if ($results = Db::getInstance()->executeS($sql))
+ {
+ foreach ($results as $result)
+ $taxes[] = new Tax((int)$result['id_tax']);
+
+ $computation_method = $result['tax_computation_method'];
+ }
+
+ return new TaxCalculator($taxes, $computation_method);
+ }
+
+ /**
+ * Save the tax calculator
+ * @since 1.5.0.1
+ * @return boolean
+ */
+ public function saveTaxCalculator()
+ {
+ // Nothing to save
+ if ($this->tax_calculator == null)
+ return true;
+
+ if (!($this->tax_calculator instanceOf TaxCalculator))
+ return false;
+
+ if (count($this->tax_calculator->taxes) == 0)
+ return true;
+
+ $values = '';
+ foreach ($this->tax_calculator->getTaxesAmount($this->unit_price_tax_excl) as $id_tax => $amount)
+ {
+ $unit_amount = (float)Tools::ps_round($amount, 2);
+ $total_amount = $unit_amount * $this->product_quantity;
+ $values .= '('.(int)$this->id.','.(float)$id_tax.','.$unit_amount.','.(float)$total_amount.'),';
+ }
+
+ $values = rtrim($values, ',');
+ $sql = 'INSERT INTO `'._DB_PREFIX_.'order_detail_tax` (id_order_detail, id_tax, unit_amount, total_amount)
+ VALUES '.$values;
+
+ return Db::getInstance()->execute($sql);
+ }
+
+ /**
+ * Get a detailed order list of an id_order
+ * @param int $id_order
+ * @return array
+ */
+ public static function getList($id_order)
+ {
+ $sql = '
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_detail`
+ WHERE `id_order` = '.(int)$id_order;
+
+ return Db::getInstance()->executeS($sql);
+ }
+
+ /*
+ * Set virtual product information
+ * @param array $product
+ */
+ protected function setVirtualProductInformation($product)
+ {
+ // Add some informations for virtual products
+ $this->download_deadline = '0000-00-00 00:00:00';
+ $this->download_hash = null;
+
+ if ($id_product_download = ProductDownload::getIdFromIdProduct((int)($product['id_product'])))
+ {
+ $productDownload = new ProductDownload((int)($id_product_download));
+ $this->download_deadline = $productDownload->getDeadLine();
+ $this->download_hash = $productDownload->getHash();
+
+ unset($productDownload);
+ }
+ }
+
+ /**
+ * Check the order state
+ * @param array $product
+ * @param int $id_order_state
+ */
+ protected function checkProductStock($product, $id_order_state)
+ {
+ if ($id_order_state != Configuration::get('PS_OS_CANCELED') && $id_order_state != Configuration::get('PS_OS_ERROR'))
+ {
+ if (StockAvailable::updateQuantity($product['id_product'], $product['id_product_attribute'], -(int)$product['cart_quantity']))
+ $product['stock_quantity'] -= $product['cart_quantity'];
+ if ($product['stock_quantity'] < 0 && Configuration::get('PS_STOCK_MANAGEMENT'))
+ $this->outOfStock = true;
+ Product::updateDefaultAttribute($product['id_product']);
+ }
+ }
+
+ /**
+ * Apply tax to the product
+ * @param object $order
+ * @param array $product
+ */
+ protected function setProductTax(Order $order, $product)
+ {
+ $this->ecotax = Tools::convertPrice(floatval($product['ecotax']), intval($order->id_currency));
+
+ // Exclude VAT
+ if (!Tax::excludeTaxeOption())
+ {
+ $id_tax_rules = (int)Product::getIdTaxRulesGroupByIdProduct((int)$product['id_product']);
+
+ $tax_manager = TaxManagerFactory::getManager($this->vat_address, $id_tax_rules);
+ $this->tax_calculator = $tax_manager->getTaxCalculator();
+ }
+
+ $this->ecotax_tax_rate = 0;
+ if (!empty($product['ecotax']))
+ $this->ecotax_tax_rate = Tax::getProductEcotaxRate($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
+
+ $this->tax_computation_method = (int)$this->tax_calculator->computation_method;
+ }
+
+ /**
+ * Set specific price of the product
+ * @param object $order
+ */
+ protected function setSpecificPrice(Order $order)
+ {
+ $this->reduction_amount = 0.00;
+ $this->reduction_percent = 0.00;
+
+ if ($this->specificPrice)
+ switch($this->specificPrice['reduction_type'])
+ {
+ case 'percentage':
+ $this->reduction_percent = (float)$this->specificPrice['reduction'] * 100;
+ break;
+ case 'amount':
+ $price = Tools::convertPrice($this->specificPrice['reduction'], $order->id_currency);
+ $this->reduction_amount = (float)(!$this->specificPrice['id_currency'] ?
+ $price : $this->specificPrice['reduction']);
+ }
+ }
+
+ /**
+ * Set detailed product price to the order detail
+ * @param object $order
+ * @param object $cart
+ * @param array $product
+ */
+ protected function setDetailProductPrice(Order $order, Cart $cart, $product)
+ {
+ $customer = new Customer((int)$order->id_customer);
+ $customer_address = new Address((int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
+
+ $this->specificPrice = SpecificPrice::getSpecificPrice((int)$product['id_product'],
+ (int)$order->id_shop,
+ (int)$order->id_currency,
+ (int)$customer_address->id_country,
+ (int)$customer->id_default_group,
+ (int)$product['cart_quantity']);
+
+ $this->product_price = (float)$product['price'];
+ $this->unit_price_tax_incl = (float)$product['price_wt'];
+ $this->unit_price_tax_excl = (float)$product['price'];
+ $this->total_price_tax_incl = (float)$product['total_wt'];
+ $this->total_price_tax_excl = (float)$product['total'];
+
+ $this->setSpecificPrice($order);
+
+ $this->group_reduction = (float)(Group::getReduction((int)($order->id_customer)));
+
+ $quantityDiscount = SpecificPrice::getQuantityDiscount((int)$product['id_product'], $this->context->shop->getID(),
+ (int)$cart->id_currency, (int)$this->vat_address->id_country,
+ (int)$this->customer->id_default_group, (int)$product['cart_quantity']);
+
+ $unitPrice = Product::getPriceStatic((int)$product['id_product'], true,
+ ($product['id_product_attribute'] ? intval($product['id_product_attribute']) : null),
+ 2, null, false, true, 1, false, (int)$order->id_customer, null, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
+
+ $this->product_quantity_discount = (float)($quantityDiscount ?
+ ((Product::getTaxCalculationMethod((int)$order->id_customer) == PS_TAX_EXC ?
+ Tools::ps_round($unitPrice, 2) : $unitPrice) - $this->tax_calculator->addTaxes($quantityDiscount['price'])) :
+ 0.00);
+
+ $this->discount_quantity_applied = (($this->specificPrice && $this->specificPrice['from_quantity'] > 1) ? 1 : 0);
+ }
+
+ /**
+ * Create an order detail liable to an id_order
+ * @param object $order
+ * @param object $cart
+ * @param array $product
+ * @param int $id_order_status
+ */
+ protected function create(Order $order, Cart $cart, $product, $id_order_state)
+ {
+ $this->tax_calculator = new TaxCalculator();
+
+ $this->id = null;
+
+ $this->product_id = (int)($product['id_product']);
+ $this->product_attribute_id = (int)($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : null);
+ $this->product_name = pSQL($product['name'].
+ ((isset($product['attributes']) && $product['attributes'] != null) ?
+ ' - '.$product['attributes'] : ''));
+
+ $this->product_quantity = (int)($product['cart_quantity']);
+ $this->product_ean13 = empty($product['ean13']) ? null : pSQL($product['ean13']);
+ $this->product_upc = empty($product['upc']) ? null : pSQL($product['upc']);
+ $this->product_reference = empty($product['reference']) ? null : pSQL($product['reference']);
+ $this->product_supplier_reference = empty($product['supplier_reference']) ? null : pSQL($product['supplier_reference']);
+ $this->product_weight = (float)$product['id_product_attribute'] ? $product['weight_attribute'] : $product['weight'];
+
+ $productQuantity = (int)(Product::getQuantity($this->product_id, $this->product_attribute_id));
+ $this->product_quantity_in_stock = ($productQuantity - (int)($product['cart_quantity']) < 0) ?
+ $productQuantity : (int)($product['cart_quantity']);
+
+ $this->setVirtualProductInformation($product);
+ $this->checkProductStock($product, $id_order_state);
+ $this->setProductTax($order, $product);
+ $this->setDetailProductPrice($order, $cart, $product);
+
+ // Add new entry to the table
+ $this->save();
+
+ $this->saveTaxCalculator();
+ unset($this->tax_calculator);
+ }
+
+ /**
+ * Create a list of order detail for a specified id_order using cart
+ * @param object $order
+ * @param object $cart
+ * @param int $id_order_status
+ */
+ public function createList(Order $order, Cart $cart, $id_order_state, $product_list)
+ {
+ $this->vat_address = new Address((int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));
+ $this->customer = new Customer((int)($order->id_customer));
+
+ $this->id_order = $order->id;
+ $products = $product_list;
+ $this->outOfStock = false;
+
+ foreach ($products as $product)
+ $this->create($order, $cart, $product, $id_order_state);
+
+ unset($this->vat_address);
+ unset($products);
+ unset($this->customer);
+ }
+
+ /**
+ * Get the state of the current stock product
+ * @return array
+ */
+ public function getStockState()
+ {
+ return $this->outOfStock;
+ }
+}
+
diff --git a/classes/order/OrderDiscount.php b/classes/order/OrderDiscount.php
new file mode 100644
index 000000000..2b493f9b2
--- /dev/null
+++ b/classes/order/OrderDiscount.php
@@ -0,0 +1,55 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderDiscountCore extends OrderCartRule
+{
+ public function __get($key)
+ {
+ Tools::displayAsDeprecated();
+ if ($key == 'id_order_discount')
+ return $this->id_order_cart_rule;
+ if ($key == 'id_discount')
+ return $this->id_cart_rule;
+ return $this->{$key};
+ }
+
+ public function __set($key, $value)
+ {
+ Tools::displayAsDeprecated();
+ if ($key == 'id_order_discount')
+ $this->id_order_cart_rule = $value;
+ if ($key == 'id_discount')
+ $this->id_cart_rule = $value;
+ $this->{$key} = $value;
+ }
+
+ public function __call($method, $args)
+ {
+ Tools::displayAsDeprecated();
+ return call_user_func_array(array($this->parent, $method), $args);
+ }
+}
\ No newline at end of file
diff --git a/classes/order/OrderHistory.php b/classes/order/OrderHistory.php
new file mode 100644
index 000000000..5749d8268
--- /dev/null
+++ b/classes/order/OrderHistory.php
@@ -0,0 +1,221 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderHistoryCore extends ObjectModel
+{
+ /** @var integer Order id */
+ public $id_order;
+
+ /** @var integer Order state id */
+ public $id_order_state;
+
+ /** @var integer Employee id for this history entry */
+ public $id_employee;
+
+ /** @var string Object creation date */
+ public $date_add;
+
+ /** @var string Object last modification date */
+ public $date_upd;
+
+ protected $tables = array ('order_history');
+
+ protected $fieldsRequired = array('id_order', 'id_order_state');
+ protected $fieldsValidate = array('id_order' => 'isUnsignedId', 'id_order_state' => 'isUnsignedId', 'id_employee' => 'isUnsignedId');
+
+ protected $table = 'order_history';
+ protected $identifier = 'id_order_history';
+
+ protected $webserviceParameters = array(
+ 'objectsNodeName' => 'order_histories',
+ 'fields' => array(
+ 'id_order_state' => array('required' => true, 'xlink_resource'=> 'order_states'),
+ 'id_order' => array('xlink_resource' => 'orders'),
+ ),
+ );
+
+ public function getFields()
+ {
+ $this->validateFields();
+
+ $fields['id_order'] = (int)$this->id_order;
+ $fields['id_order_state'] = (int)$this->id_order_state;
+ $fields['id_employee'] = (int)$this->id_employee;
+ $fields['date_add'] = pSQL($this->date_add);
+
+ return $fields;
+ }
+
+ public function changeIdOrderState($new_order_state, $id_order, $id_warehouse = null)
+ {
+ if ($new_order_state != NULL)
+ {
+ Hook::updateOrderStatus((int)($new_order_state), (int)$id_order);
+ $order = new Order((int)($id_order));
+
+ /* Best sellers */
+ $newOS = new OrderState((int)($new_order_state), $order->id_lang);
+ $oldOrderStatus = OrderHistory::getLastOrderState((int)$id_order);
+ $cart = Cart::getCartByOrderId($id_order);
+ $isValidated = $this->isValidated();
+ if (Validate::isLoadedObject($cart))
+ foreach ($cart->getProducts() as $product)
+ {
+ /* If becoming logable => adding sale */
+ if ($newOS->logable AND (!$oldOrderStatus OR !$oldOrderStatus->logable))
+ ProductSale::addProductSale($product['id_product'], $product['cart_quantity']);
+ /* If becoming unlogable => removing sale */
+ elseif (!$newOS->logable AND ($oldOrderStatus AND $oldOrderStatus->logable))
+ ProductSale::removeProductSale($product['id_product'], $product['cart_quantity']);
+
+ if (!Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && !$isValidated AND $newOS->logable AND isset($oldOrderStatus) AND $oldOrderStatus AND $oldOrderStatus->id == Configuration::get('PS_OS_ERROR'))
+ StockAvailable::updateQuantity($product['id_product'], $product['id_product_attribute'], (int)$product['cart_quantity']);
+ else if ($newOS->shipped == 1 && $oldOrderStatus->shipped == 0) // The product is removed from the physical stock. $id_warehouse is needed
+ {
+ $manager = StockManagerFactory::getManager();
+ $warehouse = new Warehouse($id_warehouse);
+
+ $manager->removeProduct($product['id_product'],
+ $product['id_product_attribute'],
+ $warehouse,
+ $product['cart_quantity'],
+ Configuration::get('PS_STOCK_CUSTOMER_ORDER_REASON'),
+ true,
+ (int)$id_order);
+ }
+ // @todo If the old order states was "shipped" and the new is "not shipped" the stock is not decremented
+ }
+
+ $this->id_order_state = (int)($new_order_state);
+
+ /* Change invoice number of order ? */
+ if (!Validate::isLoadedObject($newOS) OR !Validate::isLoadedObject($order))
+ die(Tools::displayError('Invalid new order state'));
+
+ /* The order is valid only if the invoice is available and the order is not cancelled */
+ $order->valid = $newOS->logable;
+ $order->update();
+
+ if ($newOS->invoice AND !$order->invoice_number)
+ $order->setInvoice();
+ if ($newOS->delivery AND !$order->delivery_number)
+ $order->setDelivery();
+ Hook::postUpdateOrderStatus((int)($new_order_state), (int)($id_order));
+ }
+ }
+
+ public static function getLastOrderState($id_order)
+ {
+ $id_order_state = Db::getInstance()->getValue('
+ SELECT `id_order_state`
+ FROM `'._DB_PREFIX_.'order_history`
+ WHERE `id_order` = '.(int)($id_order).'
+ ORDER BY `date_add` DESC, `id_order_history` DESC');
+ if (!$id_order_state)
+ return false;
+ return new OrderState($id_order_state, Configuration::get('PS_LANG_DEFAULT'));
+ }
+
+ public function addWithemail($autodate = true, $templateVars = false, Context $context = null)
+ {
+ if (!$context)
+ $context = Context::getContext();
+ $lastOrderState = $this->getLastOrderState($this->id_order);
+
+ if (!parent::add($autodate))
+ return false;
+
+ $result = Db::getInstance()->getRow('
+ SELECT osl.`template`, c.`lastname`, c.`firstname`, osl.`name` AS osname, c.`email`
+ FROM `'._DB_PREFIX_.'order_history` oh
+ LEFT JOIN `'._DB_PREFIX_.'orders` o ON oh.`id_order` = o.`id_order`
+ LEFT JOIN `'._DB_PREFIX_.'customer` c ON o.`id_customer` = c.`id_customer`
+ LEFT JOIN `'._DB_PREFIX_.'order_state` os ON oh.`id_order_state` = os.`id_order_state`
+ LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = o.`id_lang`)
+ WHERE oh.`id_order_history` = '.(int)($this->id).' AND os.`send_email` = 1');
+
+ if (isset($result['template']) AND Validate::isEmail($result['email']))
+ {
+ $topic = $result['osname'];
+ $data = array('{lastname}' => $result['lastname'], '{firstname}' => $result['firstname'], '{id_order}' => (int)$this->id_order);
+ if ($templateVars)
+ $data = array_merge($data, $templateVars);
+ $order = new Order((int)$this->id_order);
+ $data['{total_paid}'] = Tools::displayPrice((float)$order->total_paid, new Currency((int)$order->id_currency), false);
+ $data['{order_name}'] = sprintf("#%06d", (int)$order->id);
+
+ // An additional email is sent the first time a virtual item is validated
+ if ($virtualProducts = $order->getVirtualProducts() AND (!$lastOrderState OR !$lastOrderState->logable) AND $newOrderState = new OrderState($this->id_order_state, Configuration::get('PS_LANG_DEFAULT')) AND $newOrderState->logable)
+ {
+ $assign = array();
+ foreach ($virtualProducts AS $key => $virtualProduct)
+ {
+ $id_product_download = ProductDownload::getIdFromIdAttribute($virtualProduct['product_id'], $virtualProduct['product_attribute_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, $virtualProduct['download_hash'])
+ .'&id_order='.$order->id
+ .'&secure_key='.$order->secure_key;
+ $assign[$key]['link'] = $dl_link;
+ if ($virtualProduct['download_deadline'] != '0000-00-00 00:00:00')
+ $assign[$key]['deadline'] = Tools::displayDate($virtualProduct['download_deadline'], $order->id_lang);
+ if ($product_download->nb_downloadable != 0)
+ $assign[$key]['downloadable'] = $product_download->nb_downloadable;
+ }
+ }
+ $context->smarty->assign('virtualProducts', $assign);
+ $context->smarty->assign('id_order', $order->id);
+ $iso = Language::getIsoById((int)($order->id_lang));
+ $links = $context->smarty->fetch(_PS_MAIL_DIR_.$iso.'/download-product.tpl');
+ $tmpArray = array('{nbProducts}' => count($virtualProducts), '{virtualProducts}' => $links);
+ $data = array_merge ($data, $tmpArray);
+ // 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, $result['email'], $result['firstname'].' '.$result['lastname']);
+ }
+
+ if (Validate::isLoadedObject($order))
+ Mail::Send((int)$order->id_lang, $result['template'], $topic, $data, $result['email'], $result['firstname'].' '.$result['lastname']);
+ }
+
+ return true;
+ }
+
+ public function isValidated()
+ {
+ return Db::getInstance()->getValue('
+ SELECT COUNT(oh.`id_order_history`) AS nb
+ FROM `'._DB_PREFIX_.'order_state` os
+ LEFT JOIN `'._DB_PREFIX_.'order_history` oh ON (os.`id_order_state` = oh.`id_order_state`)
+ WHERE oh.`id_order` = '.(int)$this->id_order.'
+ AND os.`logable` = 1');
+ }
+
+}
diff --git a/classes/order/OrderMessage.php b/classes/order/OrderMessage.php
new file mode 100644
index 000000000..bef437bd9
--- /dev/null
+++ b/classes/order/OrderMessage.php
@@ -0,0 +1,78 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderMessageCore extends ObjectModel
+{
+ /** @var string name name */
+ public $name;
+
+ /** @var string message content */
+ public $message;
+
+ /** @var string Object creation date */
+ public $date_add;
+
+ protected $fieldsRequired = array();
+ protected $fieldsValidate = array();
+ protected $fieldsSize = array();
+
+ protected $fieldsRequiredLang = array('name', 'message');
+ protected $fieldsSizeLang = array('name' => 128, 'message' => 1200);
+ protected $fieldsValidateLang = array('name' => 'isGenericName', 'message' => 'isMessage');
+
+ protected $table = 'order_message';
+ protected $identifier = 'id_order_message';
+
+ protected $webserviceParameters = array(
+ 'fields' => array(
+ 'id' => array('sqlId' => 'id_discount_type', 'xlink_resource' => 'order_message_lang'),
+ 'date_add' => array('sqlId' => 'date_add')
+ )
+ );
+
+ public function getFields()
+ {
+ $this->validateFields();
+ return array('date_add' => pSQL($this->date_add));
+ }
+
+ public function getTranslationsFieldsChild()
+ {
+ $this->validateFieldsLang();
+ return $this->getTranslationsFields(array('name', 'message'));
+ }
+
+ public static function getOrderMessages($id_lang)
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT om.id_order_message, oml.name, oml.message
+ FROM '._DB_PREFIX_.'order_message om
+ LEFT JOIN '._DB_PREFIX_.'order_message_lang oml ON (oml.id_order_message = om.id_order_message)
+ WHERE oml.id_lang = '.(int)$id_lang.'
+ ORDER BY name ASC');
+ }
+}
diff --git a/classes/order/OrderReturn.php b/classes/order/OrderReturn.php
new file mode 100644
index 000000000..11e0d7de0
--- /dev/null
+++ b/classes/order/OrderReturn.php
@@ -0,0 +1,205 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderReturnCore extends ObjectModel
+{
+ /** @var integer */
+ public $id;
+
+ /** @var integer */
+ public $id_customer;
+
+ /** @var integer */
+ public $id_order;
+
+ /** @var integer */
+ public $state;
+
+ /** @var string message content */
+ public $question;
+
+ /** @var string Object creation date */
+ public $date_add;
+
+ /** @var string Object last modification date */
+ public $date_upd;
+
+ protected $tables = array ('order_return');
+
+ protected $fieldsRequired = array ('id_customer', 'id_order');
+ protected $fieldsValidate = array('id_customer' => 'isUnsignedId', 'id_order' => 'isUnsignedId', 'question' => 'isMessage');
+
+ protected $table = 'order_return';
+ protected $identifier = 'id_order_return';
+
+ public function getFields()
+ {
+ $this->validateFields();
+
+ $fields['id_customer'] = pSQL($this->id_customer);
+ $fields['id_order'] = pSQL($this->id_order);
+ $fields['state'] = pSQL($this->state);
+ $fields['date_add'] = pSQL($this->date_add);
+ $fields['date_upd'] = pSQL($this->date_upd);
+ $fields['question'] = pSQL(Tools::nl2br($this->question), true);
+ return $fields;
+ }
+
+ public function addReturnDetail($orderDetailList, $productQtyList, $customizationIds, $customizationQtyInput)
+ {
+ /* Classic product return */
+ if ($orderDetailList)
+ foreach ($orderDetailList AS $key => $orderDetail)
+ if ($qty = (int)($productQtyList[$key]))
+ Db::getInstance()->AutoExecute(_DB_PREFIX_.'order_return_detail', array('id_order_return' => (int)($this->id), 'id_order_detail' => (int)($orderDetail), 'product_quantity' => $qty, 'id_customization' => 0), 'INSERT');
+ /* Customized product return */
+ if ($customizationIds)
+ foreach ($customizationIds AS $orderDetailId => $customizations)
+ foreach ($customizations AS $customizationId)
+ if ($quantity = (int)($customizationQtyInput[(int)($customizationId)]))
+ Db::getInstance()->AutoExecute(_DB_PREFIX_.'order_return_detail', array('id_order_return' => (int)($this->id), 'id_order_detail' => (int)($orderDetailId), 'product_quantity' => $quantity, 'id_customization' => (int)($customizationId)), 'INSERT');
+ }
+
+ public function checkEnoughProduct($orderDetailList, $productQtyList, $customizationIds, $customizationQtyInput)
+ {
+ $order = new Order((int)($this->id_order));
+ if (!Validate::isLoadedObject($order))
+ die(Tools::displayError());
+ $products = $order->getProducts();
+ /* Products already returned */
+ $order_return = self::getOrdersReturn($order->id_customer, $order->id, true);
+ foreach ($order_return AS $or)
+ {
+ $order_return_products = self::getOrdersReturnProducts($or['id_order_return'], $order);
+ foreach ($order_return_products AS $key => $orp)
+ $products[$key]['product_quantity'] -= (int)($orp['product_quantity']);
+ }
+ /* Quantity check */
+ if ($orderDetailList)
+ foreach (array_keys($orderDetailList) AS $key)
+ if ($qty = (int)($productQtyList[$key]))
+ if ($products[$key]['product_quantity'] - $qty < 0)
+ return false;
+ /* Customization quantity check */
+ if ($customizationIds)
+ {
+ $orderedCustomizations = Customization::getOrderedCustomizations((int)($order->id_cart));
+ foreach ($customizationIds AS $customizations)
+ foreach ($customizations AS $customizationId)
+ {
+ $customizationId = (int)($customizationId);
+ if (!isset($orderedCustomizations[$customizationId]))
+ return false;
+ $quantity = (isset($customizationQtyInput[$customizationId]) ? (int)($customizationQtyInput[$customizationId]) : 0);
+ if ((int)($orderedCustomizations[$customizationId]['quantity']) - $quantity < 0)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function countProduct()
+ {
+ if (!$data = Db::getInstance()->getRow('
+ SELECT COUNT(`id_order_return`) AS total
+ FROM `'._DB_PREFIX_.'order_return_detail`
+ WHERE `id_order_return` = '.(int)($this->id)))
+ return false;
+ return (int)($data['total']);
+ }
+
+ static public function getOrdersReturn($customer_id, $order_id = false, $no_denied = false, Context $context = null)
+ {
+ if (!$context)
+ $context = Context::getContext();
+ $data = Db::getInstance()->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_return`
+ WHERE `id_customer` = '.(int)($customer_id).
+ ($order_id ? ' AND `id_order` = '.(int)($order_id) : '').
+ ($no_denied ? ' AND `state` != 4' : '').'
+ ORDER BY `date_add` DESC');
+ foreach ($data AS $k => $or)
+ {
+ $state = new OrderReturnState($or['state']);
+ $data[$k]['state_name'] = $state->name[$context->language->id];
+ }
+ return $data;
+ }
+
+ public static function getOrdersReturnDetail($id_order_return)
+ {
+ return Db::getInstance()->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_return_detail`
+ WHERE `id_order_return` = '.(int)($id_order_return));
+ }
+
+ public static function getOrdersReturnProducts($orderReturnId, $order)
+ {
+ $productsRet = self::getOrdersReturnDetail($orderReturnId);
+ $products = $order->getProducts();
+ $tmp = array();
+ foreach ($productsRet AS $return_detail)
+ {
+ $tmp[$return_detail['id_order_detail']]['quantity'] = isset($tmp[$return_detail['id_order_detail']]['quantity']) ? $tmp[$return_detail['id_order_detail']]['quantity'] + (int)($return_detail['product_quantity']) : (int)($return_detail['product_quantity']);
+ $tmp[$return_detail['id_order_detail']]['customizations'] = (int)($return_detail['id_customization']);
+ }
+ $resTab = array();
+ foreach ($products AS $key => $product)
+ if (isset($tmp[$product['id_order_detail']]))
+ {
+ $resTab[$key] = $product;
+ $resTab[$key]['product_quantity'] = $tmp[$product['id_order_detail']]['quantity'];
+ $resTab[$key]['customizations'] = $tmp[$product['id_order_detail']]['customizations'];
+ }
+ return $resTab;
+ }
+
+ public static function getReturnedCustomizedProducts($id_order)
+ {
+ $returns = Customization::getReturnedCustomizations($id_order);
+ $order = new Order((int)($id_order));
+ if (!Validate::isLoadedObject($order))
+ die(Tools::displayError());
+ $products = $order->getProducts();
+ foreach ($returns AS &$return)
+ {
+ $return['product_id'] = (int)($products[(int)($return['id_order_detail'])]['product_id']);
+ $return['product_attribute_id'] = (int)($products[(int)($return['id_order_detail'])]['product_attribute_id']);
+ $return['name'] = $products[(int)($return['id_order_detail'])]['product_name'];
+ $return['reference'] = $products[(int)($return['id_order_detail'])]['product_reference'];
+ }
+ return $returns;
+ }
+
+ public static function deleteOrderReturnDetail($id_order_return, $id_order_detail, $id_customization = 0)
+ {
+ return Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'order_return_detail` WHERE `id_order_detail` = '.(int)($id_order_detail).' AND `id_order_return` = '.(int)($id_order_return).' AND `id_customization` = '.(int)($id_customization));
+ }
+}
+
diff --git a/classes/order/OrderReturnState.php b/classes/order/OrderReturnState.php
new file mode 100644
index 000000000..fb40e70f5
--- /dev/null
+++ b/classes/order/OrderReturnState.php
@@ -0,0 +1,77 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderReturnStateCore extends ObjectModel
+{
+ /** @var string Name */
+ public $name;
+
+ /** @var string Display state in the specified color */
+ public $color;
+
+ protected $fieldsValidate = array('color' => 'isColor');
+ protected $fieldsRequiredLang = array('name');
+ protected $fieldsSizeLang = array('name' => 64);
+ protected $fieldsValidateLang = array('name' => 'isGenericName');
+
+ protected $table = 'order_return_state';
+ protected $identifier = 'id_order_return_state';
+
+ public function getFields()
+ {
+ $this->validateFields();
+ $fields['color'] = pSQL($this->color);
+ return $fields;
+ }
+
+ /**
+ * Check then return multilingual fields for database interaction
+ *
+ * @return array Multilingual fields
+ */
+ public function getTranslationsFieldsChild()
+ {
+ $this->validateFieldsLang();
+ return $this->getTranslationsFields(array('name'));
+ }
+
+ /**
+ * Get all available order states
+ *
+ * @param integer $id_lang Language id for state name
+ * @return array Order states
+ */
+ public static function getOrderReturnStates($id_lang)
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_return_state` ors
+ LEFT JOIN `'._DB_PREFIX_.'order_return_state_lang` orsl ON (ors.`id_order_return_state` = orsl.`id_order_return_state` AND orsl.`id_lang` = '.(int)($id_lang).')
+ ORDER BY ors.`id_order_return_state` ASC');
+ }
+}
+
diff --git a/classes/order/OrderSlip.php b/classes/order/OrderSlip.php
new file mode 100644
index 000000000..1dcfc511d
--- /dev/null
+++ b/classes/order/OrderSlip.php
@@ -0,0 +1,180 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderSlipCore extends ObjectModel
+{
+ /** @var integer */
+ public $id;
+
+ /** @var integer */
+ public $id_customer;
+
+ /** @var integer */
+ public $id_order;
+
+ /** @var float */
+ public $conversion_rate;
+
+ /** @var integer */
+ public $shipping_cost;
+
+ /** @var string Object creation date */
+ public $date_add;
+
+ /** @var string Object last modification date */
+ public $date_upd;
+
+ protected $tables = array ('order_slip');
+
+ protected $fieldsRequired = array ('id_customer', 'id_order', 'conversion_rate');
+ protected $fieldsValidate = array('id_customer' => 'isUnsignedId', 'id_order' => 'isUnsignedId', 'conversion_rate' => 'isFloat');
+
+ protected $table = 'order_slip';
+ protected $identifier = 'id_order_slip';
+
+ public function getFields()
+ {
+ $this->validateFields();
+
+ $fields['id_customer'] = (int)($this->id_customer);
+ $fields['id_order'] = (int)($this->id_order);
+ $fields['conversion_rate'] = (float)($this->conversion_rate);
+ $fields['shipping_cost'] = (int)($this->shipping_cost);
+ $fields['date_add'] = pSQL($this->date_add);
+ $fields['date_upd'] = pSQL($this->date_upd);
+ return $fields;
+ }
+
+ public function addSlipDetail($orderDetailList, $productQtyList)
+ {
+ foreach ($orderDetailList as $key => $orderDetail)
+ {
+ if ($qty = (int)($productQtyList[$key]))
+ Db::getInstance()->AutoExecute(_DB_PREFIX_.'order_slip_detail', array('id_order_slip' => (int)($this->id), 'id_order_detail' => (int)($orderDetail), 'product_quantity' => $qty), 'INSERT');
+ }
+ }
+
+ public static function getOrdersSlip($customer_id, $order_id = false)
+ {
+ return Db::getInstance()->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_slip`
+ WHERE `id_customer` = '.(int)($customer_id).
+ ($order_id ? ' AND `id_order` = '.(int)($order_id) : '').'
+ ORDER BY `date_add` DESC');
+ }
+
+ public static function getOrdersSlipDetail($id_order_slip = true, $id_order_detail = false)
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
+ ($id_order_detail ? 'SELECT SUM(`product_quantity`) AS `total`' : 'SELECT *').
+ 'FROM `'._DB_PREFIX_.'order_slip_detail`'
+ .($id_order_slip ? ' WHERE `id_order_slip` = '.(int)($id_order_slip) : '')
+ .($id_order_detail ? ' WHERE `id_order_detail` = '.(int)($id_order_detail) : ''));
+ }
+
+ public static function getOrdersSlipProducts($orderSlipId, $order)
+ {
+ $cart_rules = $order->getCartRules(true);
+ $productsRet = self::getOrdersSlipDetail($orderSlipId);
+ $products = $order->getProductsDetail();
+
+ $tmp = array();
+ foreach ($productsRet as $slip_detail)
+ $tmp[$slip_detail['id_order_detail']] = $slip_detail['product_quantity'];
+ $resTab = array();
+ foreach ($products as $key => $product)
+ if (isset($tmp[$product['id_order_detail']]))
+ {
+ $resTab[$key] = $product;
+ $resTab[$key]['product_quantity'] = $tmp[$product['id_order_detail']];
+ if (sizeof($cart_rules))
+ {
+ $order->setProductPrices($product);
+ $realProductPrice = $resTab[$key]['product_price'];
+ // Todo : must be updated to use the cart rules
+ foreach ($cart_rules as $cart_rule)
+ {
+ if ($cart_rule['reduction_percent'])
+ $resTab[$key]['product_price'] -= $realProductPrice * ($cart_rule['reduction_percent'] / 100);
+ elseif ($cart_rule['reduction_amount'])
+ $resTab[$key]['product_price'] -= (($cart_rule['reduction_amount'] * ($product['product_price_wt'] / $order->total_products_wt)) / (1.00 + ($product['tax_rate'] / 100)));
+ }
+
+ }
+ }
+ return $order->getProducts($resTab);
+ }
+
+ public function getProducts()
+ {
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT *, osd.product_quantity
+ FROM `'._DB_PREFIX_.'order_slip_detail` osd
+ INNER JOIN `'._DB_PREFIX_.'order_detail` od ON osd.id_order_detail = od.id_order_detail
+ WHERE osd.`id_order_slip` = '.(int)$this->id);
+
+ $order = new Order($this->id_order);
+ $products = array();
+ foreach ($result AS $row)
+ {
+ $order->setProductPrices($row);
+ $products[] = $row;
+ }
+ return $products;
+ }
+
+ public static function getSlipsIdByDate($dateFrom, $dateTo)
+ {
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT `id_order_slip`
+ FROM `'._DB_PREFIX_.'order_slip`
+ WHERE `date_add` BETWEEN \''.pSQL($dateFrom).' 00:00:00\' AND \''.pSQL($dateTo).' 23:59:59\'
+ ORDER BY `date_add` ASC');
+
+ $slips = array();
+ foreach ($result AS $slip)
+ $slips[] = (int)$slip['id_order_slip'];
+ return $slips;
+ }
+
+ public static function createOrderSlip($order, $productList, $qtyList, $shipping_cost = false)
+ {
+ $currency = new Currency($order->id_currency);
+ $orderSlip = new OrderSlip();
+ $orderSlip->id_customer = (int)($order->id_customer);
+ $orderSlip->id_order = (int)($order->id);
+ $orderSlip->shipping_cost = (int)($shipping_cost);
+ $orderSlip->conversion_rate = $currency->conversion_rate;
+ if (!$orderSlip->add())
+ return false;
+
+ $orderSlip->addSlipDetail($productList, $qtyList);
+ return true;
+ }
+}
+
diff --git a/classes/order/OrderState.php b/classes/order/OrderState.php
new file mode 100644
index 000000000..a6beafb38
--- /dev/null
+++ b/classes/order/OrderState.php
@@ -0,0 +1,143 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision: 6844 $
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class OrderStateCore extends ObjectModel
+{
+ /** @var string Name */
+ public $name;
+
+ /** @var string Template name if there is any e-mail to send */
+ public $template;
+
+ /** @var boolean Send an e-mail to customer ? */
+ public $send_email;
+
+ /** @var boolean Allow customer to view and download invoice when order is at this state */
+ public $invoice;
+
+ /** @var string Display state in the specified color */
+ public $color;
+
+ public $unremovable;
+
+ /** @var boolean Log authorization */
+ public $logable;
+
+ /** @var boolean Delivery */
+ public $delivery;
+
+ /** @var boolean Hidden */
+ public $hidden;
+
+ /** @var boolean Shipped */
+ public $shipped;
+
+ protected $fieldsValidate = array(
+ 'send_email' => 'isBool',
+ 'invoice' => 'isBool',
+ 'color' => 'isColor',
+ 'logable' => 'isBool',
+ 'shipped' => 'isBool'
+ );
+
+ protected $fieldsRequiredLang = array('name');
+ protected $fieldsSizeLang = array('name' => 64, 'template' => 64);
+ protected $fieldsValidateLang = array('name' => 'isGenericName', 'template' => 'isTplName');
+
+ protected $table = 'order_state';
+ protected $identifier = 'id_order_state';
+
+ protected $webserviceParameters = array(
+ 'fields' => array(
+ 'unremovable' => array(),
+ 'delivery' => array(),
+ 'hidden' => array(),
+ ),
+ );
+
+ public function getFields()
+ {
+ $this->validateFields();
+ $fields['send_email'] = (int)$this->send_email;
+ $fields['invoice'] = (int)$this->invoice;
+ $fields['color'] = pSQL($this->color);
+ $fields['unremovable'] = (int)$this->unremovable;
+ $fields['logable'] = (int)$this->logable;
+ $fields['delivery'] = (int)$this->delivery;
+ $fields['hidden'] = (int)$this->hidden;
+ $fields['shipped'] = (int)$this->shipped;
+ return $fields;
+ }
+
+ /**
+ * Check then return multilingual fields for database interaction
+ *
+ * @return array Multilingual fields
+ */
+ public function getTranslationsFieldsChild()
+ {
+ $this->validateFieldsLang();
+ return $this->getTranslationsFields(array('name', 'template'));
+ }
+
+ /**
+ * Get all available order states
+ *
+ * @param integer $id_lang Language id for state name
+ * @return array Order states
+ */
+ public static function getOrderStates($id_lang)
+ {
+ return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
+ SELECT *
+ FROM `'._DB_PREFIX_.'order_state` os
+ LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl ON (os.`id_order_state` = osl.`id_order_state` AND osl.`id_lang` = '.(int)$id_lang.')
+ ORDER BY `name` ASC');
+ }
+
+ /**
+ * Check if we can make a facture when order is in this state
+ *
+ * @param integer $id_order_state State ID
+ * @return boolean availability
+ */
+ public static function invoiceAvailable($id_order_state)
+ {
+ $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
+ SELECT `invoice` AS ok
+ FROM `'._DB_PREFIX_.'order_state`
+ WHERE `id_order_state` = '.(int)$id_order_state);
+ return $result['ok'];
+ }
+
+ public function isRemovable()
+ {
+ return !($this->unremovable);
+ }
+}
+
+