diff --git a/classes/Order.php b/classes/Order.php index 55ee2d6ad..98726bf9d 100644 --- a/classes/Order.php +++ b/classes/Order.php @@ -1042,4 +1042,13 @@ class OrderCore extends ObjectModel DELETE FROM `'._DB_PREFIX_.'order_detail` WHERE `id_order` = '.(int)($this->id)) !== false); } -} \ No newline at end of file + + /* + ** Get the an order detail list of the current order + */ + public function getOrderDetailList() + { + return OrderDetail::getList($this->id); + } + +} diff --git a/classes/OrderDetail.php b/classes/OrderDetail.php index fd5938d64..4afe0b61a 100644 --- a/classes/OrderDetail.php +++ b/classes/OrderDetail.php @@ -111,35 +111,42 @@ class OrderDetailCore extends ObjectModel /** @var date */ public $download_deadline; - protected $tables = array ('order_detail'); + protected $tables = array('order_detail'); - protected $fieldsRequired = array ('id_order', 'product_name', 'product_quantity', 'product_price', 'tax_rate'); + 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', - 'download_nb' => 'isInt', + 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' ); protected $table = 'order_detail'; @@ -157,8 +164,28 @@ class OrderDetailCore extends ObjectModel 'download_deadline' => array() ) ); + + /** @var bool */ + private $_outOfStock = false; + + /** @var TaxCalculator object */ + private $_tax_calculator = NULL; + + /** @var Address object */ + private $_vat_address = NULL; + + /** @var Address object */ + private $_specificPrice = NULL; + + private $_customer = NULL; + + private $_context = NULL; - + public function __construct($context = NULL) + { + $this->_context = $context; + } + public function getFields() { $this->validateFields(); @@ -268,6 +295,196 @@ class OrderDetailCore extends ObjectModel VALUES '.$values; return Db::getInstance()->execute($sql); + } + + /* + ** Get a detailed order list of an id_order + */ + 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 + */ + private 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 + */ + private function _checkProductStock($product, $id_order_state) + { + if ($id_order_state != Configuration::get('PS_OS_CANCELED') AND $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 + */ + private 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 + */ + private function _setSpecificPrice(Order $order) + { + $this->reduction_amont = 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_amont = (float)(!$this->_specificPrice['id_currency'] ? + $price : $this->_specificPrice['reduction']); + } + } + + /* + ** Set detailed product price to the order detail + */ + private function _setDetailProductPrice(Order $order, Cart $cart, $product) + { + $this->_specificPrice = NULL; + + $this->product_price = (float)Product::getPriceStatic((int)($product['id_product']), false, + ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), + (Product::getTaxCalculationMethod((int)($order->id_customer)) == PS_TAX_EXC ? 2 : 6), + NULL, false, false, $product['cart_quantity'], false, (int)($order->id_customer), + (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}), $this->_specificPrice, false, false); + + $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 AND $this->_specificPrice['from_quantity'] > 1) ? 1 : 0); + } + + /* + ** Create an order detail liable to an id_order + */ + private 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']) AND $product['attributes'] != NULL) ? + ' - '.$product['attributes'] : '')); + + $this->product_quantity = (int)($product['cart_quantity']); + $this->product_en13 = 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(); + + OrderDetail::saveTaxCalculatorStatic($this->id, $this->_tax_calculator); + unset($this->_tax_calculator); + } + + /* + ** Create a list of order detail for a specified id_order using cart + */ + public function createList(Order $order, Cart $cart, $id_order_state) + { + $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 = $cart->getProducts(); + $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 + */ + public function getStockState() + { + return $this->_oufOfStock; } } diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index 95eeb9707..84cff28b4 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -109,7 +109,6 @@ abstract class PaymentModuleCore extends Module $order->id_customer = (int)($cart->id_customer); $order->id_address_invoice = (int)($cart->id_address_invoice); $order->id_address_delivery = (int)($cart->id_address_delivery); - $vat_address = new Address((int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); $order->id_currency = ($currency_special ? (int)($currency_special) : (int)($cart->id_currency)); $order->id_lang = (int)($cart->id_lang); $order->id_cart = (int)($cart->id); @@ -176,84 +175,19 @@ abstract class PaymentModuleCore extends Module } } + // Insert new Order detail list using cart for the current order + $orderDetail = new OrderDetail($this->context); + $orderDetail->createList($order, $cart, $id_order_state); + // Insert products from cart into order_detail table - $products = $cart->getProducts(); $productsList = ''; - $db = Db::getInstance(); - $query = 'INSERT INTO `'._DB_PREFIX_.'order_detail` - (`id_order`, `product_id`, `product_attribute_id`, `product_name`, `product_quantity`, `product_quantity_in_stock`, `product_price`, `reduction_percent`, `reduction_amount`, `group_reduction`, `product_quantity_discount`, `product_ean13`, `product_upc`, `product_reference`, `product_supplier_reference`, `product_weight`, `tax_computation_method`, `ecotax`, `ecotax_tax_rate`, `discount_quantity_applied`, `download_deadline`, `download_hash`) - VALUES '; - - $customizedDatas = Product::getAllCustomizedDatas((int)($order->id_cart)); - Product::addCustomizationPrice($products, $customizedDatas); - $outOfStock = false; + $products = $cart->getProducts(); foreach ($products AS $key => $product) { - $productQuantity = (int)(Product::getQuantity((int)($product['id_product']), ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL))); - $quantityInStock = ($productQuantity - (int)($product['cart_quantity']) < 0) ? $productQuantity : (int)($product['cart_quantity']); - if ($id_order_state != Configuration::get('PS_OS_CANCELED') AND $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')) - $outOfStock = true; - Product::updateDefaultAttribute($product['id_product']); - } $price = Product::getPriceStatic((int)($product['id_product']), false, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 6, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - $price_wt = Product::getPriceStatic((int)($product['id_product']), true, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 2, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); - // Add some informations for virtual products - $deadline = '0000-00-00 00:00:00'; - $download_hash = NULL; - if ($id_product_download = ProductDownload::getIdFromIdProduct((int)($product['id_product']))) - { - $productDownload = new ProductDownload((int)($id_product_download)); - $deadline = $productDownload->getDeadLine(); - $download_hash = $productDownload->getHash(); - } - - // Exclude VAT - $tax_calculator = new TaxCalculator(); - if (!Tax::excludeTaxeOption()) - { - $address = Address::initialize($vat_address->id); - $id_tax_rules = (int)Product::getIdTaxRulesGroupByIdProduct((int)$product['id_product']); - - $tax_manager = TaxManagerFactory::getManager($vat_address, $id_tax_rules); - $tax_calculator = $tax_manager->getTaxCalculator(); - } - - $ecotaxTaxRate = 0; - if (!empty($product['ecotax'])) - $ecotaxTaxRate = Tax::getProductEcotaxRate($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}); - - $product_price = (float)Product::getPriceStatic((int)($product['id_product']), false, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), (Product::getTaxCalculationMethod((int)($order->id_customer)) == PS_TAX_EXC ? 2 : 6), NULL, false, false, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}), $specificPrice, false, false); - $quantityDiscount = SpecificPrice::getQuantityDiscount((int)$product['id_product'], $this->context->shop->getID(), (int)$cart->id_currency, (int)$vat_address->id_country, (int)$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')}); - $quantityDiscountValue = $quantityDiscount ? ((Product::getTaxCalculationMethod((int)$order->id_customer) == PS_TAX_EXC ? Tools::ps_round($unitPrice, 2) : $unitPrice) - $tax_calculator->addTaxes($quantityDiscount['price'])) : 0.00; - $query .= '('.(int)($order->id).', - '.(int)($product['id_product']).', - '.(isset($product['id_product_attribute']) ? (int)($product['id_product_attribute']) : 'NULL').', - \''.pSQL($product['name'].((isset($product['attributes']) AND $product['attributes'] != NULL) ? ' - '.$product['attributes'] : '')).'\', - '.(int)($product['cart_quantity']).', - '.$quantityInStock.', - '.$product_price.', - '.(float)(($specificPrice AND $specificPrice['reduction_type'] == 'percentage') ? $specificPrice['reduction'] * 100 : 0.00).', - '.(float)(($specificPrice AND $specificPrice['reduction_type'] == 'amount') ? (!$specificPrice['id_currency'] ? Tools::convertPrice($specificPrice['reduction'], $order->id_currency) : $specificPrice['reduction']) : 0.00).', - '.(float)(Group::getReduction((int)($order->id_customer))).', - '.$quantityDiscountValue.', - '.(empty($product['ean13']) ? 'NULL' : '\''.pSQL($product['ean13']).'\'').', - '.(empty($product['upc']) ? 'NULL' : '\''.pSQL($product['upc']).'\'').', - '.(empty($product['reference']) ? 'NULL' : '\''.pSQL($product['reference']).'\'').', - '.(empty($product['supplier_reference']) ? 'NULL' : '\''.pSQL($product['supplier_reference']).'\'').', - '.(float)($product['id_product_attribute'] ? $product['weight_attribute'] : $product['weight']).', - '.(int)$tax_calculator->computation_method.', - '.(float)Tools::convertPrice(floatval($product['ecotax']), intval($order->id_currency)).', - '.(float)$ecotaxTaxRate.', - '.(($specificPrice AND $specificPrice['from_quantity'] > 1) ? 1 : 0).', - \''.pSQL($deadline).'\', - \''.pSQL($download_hash).'\'),'; - + $price_wt = Product::getPriceStatic((int)($product['id_product']), true, ($product['id_product_attribute'] ? (int)($product['id_product_attribute']) : NULL), 2, NULL, false, true, $product['cart_quantity'], false, (int)($order->id_customer), (int)($order->id_cart), (int)($order->{Configuration::get('PS_TAX_ADDRESS_TYPE')})); + $customizationQuantity = 0; if (isset($customizedDatas[$product['id_product']][$product['id_product_attribute']])) { @@ -293,12 +227,7 @@ abstract class PaymentModuleCore extends Module