From e4096bf9436204f0f58bb1d1cbbeda9326309551 Mon Sep 17 00:00:00 2001 From: rGaillard Date: Sun, 20 Nov 2011 15:51:33 +0000 Subject: [PATCH] // Constructor of OrderDetail correctly call parent constructor git-svn-id: http://dev.prestashop.com/svn/v1/branches/1.5.x@10291 b9a71923-0436-4b27-9f14-aed3839534dd --- classes/PaymentModule.php | 4 +- classes/order/Order.php | 1346 ++++++++++++++++++++++++++++ classes/order/OrderCartRule.php | 71 ++ classes/order/OrderDetail.php | 555 ++++++++++++ classes/order/OrderDiscount.php | 55 ++ classes/order/OrderHistory.php | 221 +++++ classes/order/OrderMessage.php | 78 ++ classes/order/OrderReturn.php | 205 +++++ classes/order/OrderReturnState.php | 77 ++ classes/order/OrderSlip.php | 180 ++++ classes/order/OrderState.php | 143 +++ 11 files changed, 2933 insertions(+), 2 deletions(-) create mode 100644 classes/order/Order.php create mode 100644 classes/order/OrderCartRule.php create mode 100644 classes/order/OrderDetail.php create mode 100644 classes/order/OrderDiscount.php create mode 100644 classes/order/OrderHistory.php create mode 100644 classes/order/OrderMessage.php create mode 100644 classes/order/OrderReturn.php create mode 100644 classes/order/OrderReturnState.php create mode 100644 classes/order/OrderSlip.php create mode 100644 classes/order/OrderState.php 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 'invoice'; + else + echo ' '; + echo ''; + echo ''; + if ($orderState->delivery AND $order->delivery_number) + echo 'delivery'; + 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); + } +} + +