PageRenderTime 114ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/PaymentModule.php

https://bitbucket.org/yhjohn/ayanapure.com
PHP | 817 lines | 584 code | 102 blank | 131 comment | 111 complexity | 91d937b0c8cdaece649585cfd082cfe9 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0
  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @version Release: $Revision: 6844 $
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. * International Registered Trademark & Property of PrestaShop SA
  26. */
  27. abstract class PaymentModuleCore extends Module
  28. {
  29. /** @var integer Current order's id */
  30. public $currentOrder;
  31. public $currencies = true;
  32. public $currencies_mode = 'checkbox';
  33. public function install()
  34. {
  35. if (!parent::install())
  36. return false;
  37. // Insert currencies availability
  38. if ($this->currencies_mode == 'checkbox')
  39. {
  40. if (!$this->addCheckboxCurrencyRestrictionsForModule())
  41. return false;
  42. }
  43. elseif ($this->currencies_mode == 'radio')
  44. {
  45. if (!$this->addRadioCurrencyRestrictionsForModule())
  46. return false;
  47. }
  48. else
  49. Tools::displayError('No currency mode for payment module');
  50. // Insert countries availability
  51. $return = $this->addCheckboxCountryRestrictionsForModule();
  52. return $return;
  53. }
  54. public function uninstall()
  55. {
  56. if (!Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_country` WHERE id_module = '.(int)$this->id)
  57. || !Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_currency` WHERE id_module = '.(int)$this->id)
  58. || !Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_group` WHERE id_module = '.(int)$this->id))
  59. return false;
  60. return parent::uninstall();
  61. }
  62. /**
  63. * Add checkbox currency restrictions for a new module
  64. * @param integer id_module
  65. * @param array $shops
  66. */
  67. public function addCheckboxCurrencyRestrictionsForModule(array $shops = array())
  68. {
  69. if (!$shops)
  70. $shops = Shop::getShops(true, null, true);
  71. foreach ($shops as $s)
  72. {
  73. if (!Db::getInstance()->execute('
  74. INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_shop`, `id_currency`)
  75. SELECT '.(int)$this->id.', "'.(int)$s.'", `id_currency` FROM `'._DB_PREFIX_.'currency` WHERE deleted = 0'))
  76. return false;
  77. }
  78. return true;
  79. }
  80. /**
  81. * Add radio currency restrictions for a new module
  82. * @param integer id_module
  83. * @param array $shops
  84. */
  85. public function addRadioCurrencyRestrictionsForModule(array $shops = array())
  86. {
  87. if (!$shops)
  88. $shops = Shop::getShops(true, null, true);
  89. foreach ($shops as $s)
  90. if (!Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_shop`, `id_currency`)
  91. VALUES ('.(int)$this->id.', "'.(int)$s.'", -2)'))
  92. return false;
  93. return true;
  94. }
  95. /**
  96. * Add checkbox country restrictions for a new module
  97. * @param integer id_module
  98. * @param array $shops
  99. */
  100. public function addCheckboxCountryRestrictionsForModule(array $shops = array())
  101. {
  102. $countries = Country::getCountries((int)Context::getContext()->language->id, true); //get only active country
  103. $country_ids = array();
  104. foreach ($countries as $country)
  105. $country_ids[] = $country['id_country'];
  106. return Country::addModuleRestrictions($shops, $countries, array(array('id_module' => (int)$this->id)));
  107. }
  108. /**
  109. * Validate an order in database
  110. * Function called from a payment module
  111. *
  112. * @param integer $id_cart Value
  113. * @param integer $id_order_state Value
  114. * @param float $amount_paid Amount really paid by customer (in the default currency)
  115. * @param string $payment_method Payment method (eg. 'Credit card')
  116. * @param string $message Message to attach to order
  117. */
  118. public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown',
  119. $message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false,
  120. $secure_key = false, Shop $shop = null)
  121. {
  122. $this->context->cart = new Cart($id_cart);
  123. $this->context->customer = new Customer($this->context->cart->id_customer);
  124. $this->context->language = new Language($this->context->cart->id_lang);
  125. $this->context->shop = ($shop ? $shop : new Shop($this->context->cart->id_shop));
  126. $id_currency = $currency_special ? (int)$currency_special : (int)$this->context->cart->id_currency;
  127. $this->context->currency = new Currency($id_currency, null, $this->context->shop->id);
  128. if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
  129. $context_country = $this->context->country;
  130. $order_status = new OrderState((int)$id_order_state, (int)$this->context->language->id);
  131. if (!Validate::isLoadedObject($order_status))
  132. throw new PrestaShopException('Can\'t load Order state status');
  133. if (!$this->active)
  134. die(Tools::displayError());
  135. // Does order already exists ?
  136. if (Validate::isLoadedObject($this->context->cart) && $this->context->cart->OrderExists() == false)
  137. {
  138. if ($secure_key !== false && $secure_key != $this->context->cart->secure_key)
  139. die(Tools::displayError());
  140. // For each package, generate an order
  141. $delivery_option_list = $this->context->cart->getDeliveryOptionList();
  142. $package_list = $this->context->cart->getPackageList();
  143. $cart_delivery_option = $this->context->cart->getDeliveryOption();
  144. // If some delivery options are not defined, or not valid, use the first valid option
  145. foreach ($delivery_option_list as $id_address => $package)
  146. if (!isset($cart_delivery_option[$id_address]) || !array_key_exists($cart_delivery_option[$id_address], $package))
  147. foreach ($package as $key => $val)
  148. {
  149. $cart_delivery_option[$id_address] = $key;
  150. break;
  151. }
  152. $order_list = array();
  153. $order_detail_list = array();
  154. $reference = Order::generateReference();
  155. $this->currentOrderReference = $reference;
  156. $order_creation_failed = false;
  157. $cart_total_paid = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH), 2);
  158. if ($this->context->cart->orderExists())
  159. {
  160. $error = Tools::displayError('An order has already been placed using this cart.');
  161. Logger::addLog($error, 4, '0000001', 'Cart', intval($this->context->cart->id));
  162. die($error);
  163. }
  164. foreach ($cart_delivery_option as $id_address => $key_carriers)
  165. foreach ($delivery_option_list[$id_address][$key_carriers]['carrier_list'] as $id_carrier => $data)
  166. foreach ($data['package_list'] as $id_package)
  167. $package_list[$id_address][$id_package]['id_carrier'] = $id_carrier;
  168. // Make sure CarRule caches are empty
  169. CartRule::cleanCache();
  170. foreach ($package_list as $id_address => $packageByAddress)
  171. foreach ($packageByAddress as $id_package => $package)
  172. {
  173. $order = new Order();
  174. $order->product_list = $package['product_list'];
  175. if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
  176. {
  177. $address = new Address($id_address);
  178. $this->context->country = new Country($address->id_country, $this->context->cart->id_lang);
  179. }
  180. $carrier = null;
  181. if (!$this->context->cart->isVirtualCart() && isset($package['id_carrier']))
  182. {
  183. $carrier = new Carrier($package['id_carrier'], $this->context->cart->id_lang);
  184. $order->id_carrier = (int)$carrier->id;
  185. $id_carrier = (int)$carrier->id;
  186. }
  187. else
  188. {
  189. $order->id_carrier = 0;
  190. $id_carrier = 0;
  191. }
  192. $order->id_customer = (int)$this->context->cart->id_customer;
  193. $order->id_address_invoice = (int)$this->context->cart->id_address_invoice;
  194. $order->id_address_delivery = (int)$id_address;
  195. $order->id_currency = $this->context->currency->id;
  196. $order->id_lang = (int)$this->context->cart->id_lang;
  197. $order->id_cart = (int)$this->context->cart->id;
  198. $order->reference = $reference;
  199. $order->id_shop = (int)$this->context->shop->id;
  200. $order->id_shop_group = (int)$this->context->shop->id_shop_group;
  201. $order->secure_key = ($secure_key ? pSQL($secure_key) : pSQL($this->context->customer->secure_key));
  202. $order->payment = $payment_method;
  203. if (isset($this->name))
  204. $order->module = $this->name;
  205. $order->recyclable = $this->context->cart->recyclable;
  206. $order->gift = (int)$this->context->cart->gift;
  207. $order->gift_message = $this->context->cart->gift_message;
  208. $order->conversion_rate = $this->context->currency->conversion_rate;
  209. $amount_paid = !$dont_touch_amount ? Tools::ps_round((float)$amount_paid, 2) : $amount_paid;
  210. $order->total_paid_real = 0;
  211. $order->total_products = (float)$this->context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
  212. $order->total_products_wt = (float)$this->context->cart->getOrderTotal(true, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
  213. $order->total_discounts_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
  214. $order->total_discounts_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
  215. $order->total_discounts = $order->total_discounts_tax_incl;
  216. $order->total_shipping_tax_excl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, false, null, $order->product_list);
  217. $order->total_shipping_tax_incl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, true, null, $order->product_list);
  218. $order->total_shipping = $order->total_shipping_tax_incl;
  219. if (!is_null($carrier) && Validate::isLoadedObject($carrier))
  220. $order->carrier_tax_rate = $carrier->getTaxesRate(new Address($this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));
  221. $order->total_wrapping_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
  222. $order->total_wrapping_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
  223. $order->total_wrapping = $order->total_wrapping_tax_incl;
  224. $order->total_paid_tax_excl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(false, Cart::BOTH, $order->product_list, $id_carrier), 2);
  225. $order->total_paid_tax_incl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH, $order->product_list, $id_carrier), 2);
  226. $order->total_paid = $order->total_paid_tax_incl;
  227. $order->invoice_date = '0000-00-00 00:00:00';
  228. $order->delivery_date = '0000-00-00 00:00:00';
  229. // Creating order
  230. $result = $order->add();
  231. if (!$result)
  232. throw new PrestaShopException('Can\'t save Order');
  233. // Amount paid by customer is not the right one -> Status = payment error
  234. // We don't use the following condition to avoid the float precision issues : http://www.php.net/manual/en/language.types.float.php
  235. // if ($order->total_paid != $order->total_paid_real)
  236. // We use number_format in order to compare two string
  237. if ($order_status->logable && number_format($cart_total_paid, 2) != number_format($amount_paid, 2))
  238. $id_order_state = Configuration::get('PS_OS_ERROR');
  239. $order_list[] = $order;
  240. // Insert new Order detail list using cart for the current order
  241. $order_detail = new OrderDetail(null, null, $this->context);
  242. $order_detail->createList($order, $this->context->cart, $id_order_state, $order->product_list, 0, true, $package_list[$id_address][$id_package]['id_warehouse']);
  243. $order_detail_list[] = $order_detail;
  244. // Adding an entry in order_carrier table
  245. if (!is_null($carrier))
  246. {
  247. $order_carrier = new OrderCarrier();
  248. $order_carrier->id_order = (int)$order->id;
  249. $order_carrier->id_carrier = (int)$id_carrier;
  250. $order_carrier->weight = (float)$order->getTotalWeight();
  251. $order_carrier->shipping_cost_tax_excl = (float)$order->total_shipping_tax_excl;
  252. $order_carrier->shipping_cost_tax_incl = (float)$order->total_shipping_tax_incl;
  253. $order_carrier->add();
  254. }
  255. }
  256. // The country can only change if the address used for the calculation is the delivery address, and if multi-shipping is activated
  257. if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
  258. $this->context->country = $context_country;
  259. // Register Payment only if the order status validate the order
  260. if ($order_status->logable)
  261. {
  262. // $order is the last order loop in the foreach
  263. // The method addOrderPayment of the class Order make a create a paymentOrder
  264. // linked to the order reference and not to the order id
  265. if (isset($extra_vars['transaction_id']))
  266. $transaction_id = $extra_vars['transaction_id'];
  267. else
  268. $transaction_id = null;
  269. if (!$order->addOrderPayment($amount_paid, null, $transaction_id))
  270. throw new PrestaShopException('Can\'t save Order Payment');
  271. }
  272. // Next !
  273. $only_one_gift = false;
  274. $cart_rule_used = array();
  275. $products = $this->context->cart->getProducts();
  276. $cart_rules = $this->context->cart->getCartRules();
  277. // Make sure CarRule caches are empty
  278. CartRule::cleanCache();
  279. foreach ($order_detail_list as $key => $order_detail)
  280. {
  281. $order = $order_list[$key];
  282. if (!$order_creation_failed & isset($order->id))
  283. {
  284. if (!$secure_key)
  285. $message .= '<br />'.Tools::displayError('Warning: the secure key is empty, check your payment account before validation');
  286. // Optional message to attach to this order
  287. if (isset($message) & !empty($message))
  288. {
  289. $msg = new Message();
  290. $message = strip_tags($message, '<br>');
  291. if (Validate::isCleanHtml($message))
  292. {
  293. $msg->message = $message;
  294. $msg->id_order = intval($order->id);
  295. $msg->private = 1;
  296. $msg->add();
  297. }
  298. }
  299. // Insert new Order detail list using cart for the current order
  300. //$orderDetail = new OrderDetail(null, null, $this->context);
  301. //$orderDetail->createList($order, $this->context->cart, $id_order_state);
  302. // Construct order detail table for the email
  303. $products_list = '';
  304. $virtual_product = true;
  305. foreach ($products as $key => $product)
  306. {
  307. $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')});
  308. $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')});
  309. $customization_quantity = 0;
  310. if (isset($customized_datas[$product['id_product']][$product['id_product_attribute']]))
  311. {
  312. $customization_text = '';
  313. foreach ($customized_datas[$product['id_product']][$product['id_product_attribute']] as $customization)
  314. {
  315. if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD]))
  316. foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] as $text)
  317. $customization_text .= $text['name'].': '.$text['value'].'<br />';
  318. if (isset($customization['datas'][Product::CUSTOMIZE_FILE]))
  319. $customization_text .= sprintf(Tools::displayError('%d image(s)'), count($customization['datas'][Product::CUSTOMIZE_FILE])).'<br />';
  320. $customization_text .= '---<br />';
  321. }
  322. $customization_text = rtrim($customization_text, '---<br />');
  323. $customization_quantity = (int)$product['customizationQuantityTotal'];
  324. $products_list .=
  325. '<tr style="background-color: '.($key % 2 ? '#DDE2E6' : '#EBECEE').';">
  326. <td style="padding: 0.6em 0.4em;">'.$product['reference'].'</td>
  327. <td style="padding: 0.6em 0.4em;"><strong>'.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').' - '.Tools::displayError('Customized').(!empty($customization_text) ? ' - '.$customization_text : '').'</strong></td>
  328. <td style="padding: 0.6em 0.4em; text-align: right;">'.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt, $this->context->currency, false).'</td>
  329. <td style="padding: 0.6em 0.4em; text-align: center;">'.$customization_quantity.'</td>
  330. <td style="padding: 0.6em 0.4em; text-align: right;">'.Tools::displayPrice($customization_quantity * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt), $this->context->currency, false).'</td>
  331. </tr>';
  332. }
  333. if (!$customization_quantity || (int)$product['cart_quantity'] > $customization_quantity)
  334. $products_list .=
  335. '<tr style="background-color: '.($key % 2 ? '#DDE2E6' : '#EBECEE').';">
  336. <td style="padding: 0.6em 0.4em;">'.$product['reference'].'</td>
  337. <td style="padding: 0.6em 0.4em;"><strong>'.$product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : '').'</strong></td>
  338. <td style="padding: 0.6em 0.4em; text-align: right;">'.Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt, $this->context->currency, false).'</td>
  339. <td style="padding: 0.6em 0.4em; text-align: center;">'.((int)$product['cart_quantity'] - $customization_quantity).'</td>
  340. <td style="padding: 0.6em 0.4em; text-align: right;">'.Tools::displayPrice(((int)$product['cart_quantity'] - $customization_quantity) * (Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt), $this->context->currency, false).'</td>
  341. </tr>';
  342. // Check if is not a virutal product for the displaying of shipping
  343. if (!$product['is_virtual'])
  344. $virtual_product &= false;
  345. } // end foreach ($products)
  346. $cart_rules_list = '';
  347. foreach ($cart_rules as $cart_rule)
  348. {
  349. $package = array('id_carrier' => $order->id_carrier, 'id_address' => $order->id_address_delivery, 'products' => $order->product_list);
  350. $values = array(
  351. 'tax_incl' => $cart_rule['obj']->getContextualValue(true, $this->context, CartRule::FILTER_ACTION_ALL, $package),
  352. 'tax_excl' => $cart_rule['obj']->getContextualValue(false, $this->context, CartRule::FILTER_ACTION_ALL, $package)
  353. );
  354. // If the reduction is not applicable to this order, then continue with the next one
  355. if (!$values['tax_excl'])
  356. continue;
  357. $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values);
  358. /* IF
  359. ** - This is not multi-shipping
  360. ** - The value of the voucher is greater than the total of the order
  361. ** - Partial use is allowed
  362. ** - This is an "amount" reduction, not a reduction in % or a gift
  363. ** THEN
  364. ** The voucher is cloned with a new value corresponding to the remainder
  365. */
  366. if (count($order_list) == 1 && $values['tax_incl'] > $order->total_products_wt && $cart_rule['obj']->partial_use == 1 && $cart_rule['obj']->reduction_amount > 0)
  367. {
  368. // Create a new voucher from the original
  369. $voucher = new CartRule($cart_rule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it
  370. unset($voucher->id);
  371. // Set a new voucher code
  372. $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cart_rule['obj']->id), 0, 16) : $voucher->code.'-2';
  373. if (preg_match('/\-([0-9]{1,2})\-([0-9]{1,2})$/', $voucher->code, $matches) && $matches[1] == $matches[2])
  374. $voucher->code = preg_replace('/'.$matches[0].'$/', '-'.(intval($matches[1]) + 1), $voucher->code);
  375. // Set the new voucher value
  376. if ($voucher->reduction_tax)
  377. $voucher->reduction_amount = $values['tax_incl'] - $order->total_products_wt;
  378. else
  379. $voucher->reduction_amount = $values['tax_excl'] - $order->total_products;
  380. $voucher->id_customer = $order->id_customer;
  381. $voucher->quantity = 1;
  382. if ($voucher->add())
  383. {
  384. // If the voucher has conditions, they are now copied to the new voucher
  385. CartRule::copyConditions($cart_rule['obj']->id, $voucher->id);
  386. $params = array(
  387. '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $this->context->currency, false),
  388. '{voucher_num}' => $voucher->code,
  389. '{firstname}' => $this->context->customer->firstname,
  390. '{lastname}' => $this->context->customer->lastname,
  391. '{id_order}' => $order->reference,
  392. '{order_name}' => $order->getUniqReference()
  393. );
  394. Mail::Send(
  395. (int)$order->id_lang,
  396. 'voucher',
  397. sprintf(Mail::l('New voucher regarding your order %s', (int)$order->id_lang), $order->reference),
  398. $params,
  399. $this->context->customer->email,
  400. $this->context->customer->firstname.' '.$this->context->customer->lastname,
  401. null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop
  402. );
  403. }
  404. }
  405. if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED') && !in_array($cart_rule['obj']->id, $cart_rule_used))
  406. {
  407. $cart_rule_used[] = $cart_rule['obj']->id;
  408. // Create a new instance of Cart Rule without id_lang, in order to update its quantity
  409. $cart_rule_to_update = new CartRule($cart_rule['obj']->id);
  410. $cart_rule_to_update->quantity = max(0, $cart_rule_to_update->quantity - 1);
  411. $cart_rule_to_update->update();
  412. }
  413. $cart_rules_list .= '
  414. <tr style="background-color:#EBECEE;">
  415. <td colspan="4" style="padding:0.6em 0.4em;text-align:right">'.Tools::displayError('Voucher name:').' '.$cart_rule['obj']->name.'</td>
  416. <td style="padding:0.6em 0.4em;text-align:right">'.($values['tax_incl'] != 0.00 ? '-' : '').Tools::displayPrice($values['tax_incl'], $this->context->currency, false).'</td>
  417. </tr>';
  418. }
  419. // Specify order id for message
  420. $old_message = Message::getMessageByCartId((int)$this->context->cart->id);
  421. if ($old_message)
  422. {
  423. $update_message = new Message((int)$old_message['id_message']);
  424. $update_message->id_order = (int)$order->id;
  425. $update_message->update();
  426. // Add this message in the customer thread
  427. $customer_thread = new CustomerThread();
  428. $customer_thread->id_contact = 0;
  429. $customer_thread->id_customer = (int)$order->id_customer;
  430. $customer_thread->id_shop = (int)$this->context->shop->id;
  431. $customer_thread->id_order = (int)$order->id;
  432. $customer_thread->id_lang = (int)$this->context->language->id;
  433. $customer_thread->email = $this->context->customer->email;
  434. $customer_thread->status = 'open';
  435. $customer_thread->token = Tools::passwdGen(12);
  436. $customer_thread->add();
  437. $customer_message = new CustomerMessage();
  438. $customer_message->id_customer_thread = $customer_thread->id;
  439. $customer_message->id_employee = 0;
  440. $customer_message->message = htmlentities($update_message->message, ENT_COMPAT, 'UTF-8');
  441. $customer_message->private = 0;
  442. if (!$customer_message->add())
  443. $this->errors[] = Tools::displayError('An error occurred while saving message');
  444. }
  445. // Hook validate order
  446. Hook::exec('actionValidateOrder', array(
  447. 'cart' => $this->context->cart,
  448. 'order' => $order,
  449. 'customer' => $this->context->customer,
  450. 'currency' => $this->context->currency,
  451. 'orderStatus' => $order_status
  452. ));
  453. foreach ($this->context->cart->getProducts() as $product)
  454. if ($order_status->logable)
  455. ProductSale::addProductSale((int)$product['id_product'], (int)$product['cart_quantity']);
  456. if (Configuration::get('PS_STOCK_MANAGEMENT') && $order_detail->getStockState())
  457. {
  458. $history = new OrderHistory();
  459. $history->id_order = (int)$order->id;
  460. $history->changeIdOrderState(Configuration::get('PS_OS_OUTOFSTOCK'), $order);
  461. $history->addWithemail();
  462. }
  463. // Set order state in order history ONLY even if the "out of stock" status has not been yet reached
  464. // So you migth have two order states
  465. $new_history = new OrderHistory();
  466. $new_history->id_order = (int)$order->id;
  467. $new_history->changeIdOrderState((int)$id_order_state, $order, true);
  468. $new_history->addWithemail(true, $extra_vars);
  469. unset($order_detail);
  470. // Order is reloaded because the status just changed
  471. $order = new Order($order->id);
  472. // Send an e-mail to customer (one order = one email)
  473. if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED') && $this->context->customer->id)
  474. {
  475. $invoice = new Address($order->id_address_invoice);
  476. $delivery = new Address($order->id_address_delivery);
  477. $delivery_state = $delivery->id_state ? new State($delivery->id_state) : false;
  478. $invoice_state = $invoice->id_state ? new State($invoice->id_state) : false;
  479. $data = array(
  480. '{firstname}' => $this->context->customer->firstname,
  481. '{lastname}' => $this->context->customer->lastname,
  482. '{email}' => $this->context->customer->email,
  483. '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"),
  484. '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"),
  485. '{delivery_block_html}' => $this->_getFormatedAddress($delivery, '<br />', array(
  486. 'firstname' => '<span style="color:#DB3484; font-weight:bold;">%s</span>',
  487. 'lastname' => '<span style="color:#DB3484; font-weight:bold;">%s</span>'
  488. )),
  489. '{invoice_block_html}' => $this->_getFormatedAddress($invoice, '<br />', array(
  490. 'firstname' => '<span style="color:#DB3484; font-weight:bold;">%s</span>',
  491. 'lastname' => '<span style="color:#DB3484; font-weight:bold;">%s</span>'
  492. )),
  493. '{delivery_company}' => $delivery->company,
  494. '{delivery_firstname}' => $delivery->firstname,
  495. '{delivery_lastname}' => $delivery->lastname,
  496. '{delivery_address1}' => $delivery->address1,
  497. '{delivery_address2}' => $delivery->address2,
  498. '{delivery_city}' => $delivery->city,
  499. '{delivery_postal_code}' => $delivery->postcode,
  500. '{delivery_country}' => $delivery->country,
  501. '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '',
  502. '{delivery_phone}' => ($delivery->phone) ? $delivery->phone : $delivery->phone_mobile,
  503. '{delivery_other}' => $delivery->other,
  504. '{invoice_company}' => $invoice->company,
  505. '{invoice_vat_number}' => $invoice->vat_number,
  506. '{invoice_firstname}' => $invoice->firstname,
  507. '{invoice_lastname}' => $invoice->lastname,
  508. '{invoice_address2}' => $invoice->address2,
  509. '{invoice_address1}' => $invoice->address1,
  510. '{invoice_city}' => $invoice->city,
  511. '{invoice_postal_code}' => $invoice->postcode,
  512. '{invoice_country}' => $invoice->country,
  513. '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '',
  514. '{invoice_phone}' => ($invoice->phone) ? $invoice->phone : $invoice->phone_mobile,
  515. '{invoice_other}' => $invoice->other,
  516. '{order_name}' => $order->getUniqReference(),
  517. '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), (int)$order->id_lang, 1),
  518. '{carrier}' => $virtual_product ? Tools::displayError('No carrier') : $carrier->name,
  519. '{payment}' => Tools::substr($order->payment, 0, 32),
  520. '{products}' => $this->formatProductAndVoucherForEmail($products_list),
  521. '{discounts}' => $this->formatProductAndVoucherForEmail($cart_rules_list),
  522. '{total_paid}' => Tools::displayPrice($order->total_paid, $this->context->currency, false),
  523. '{total_products}' => Tools::displayPrice($order->total_paid - $order->total_shipping - $order->total_wrapping + $order->total_discounts, $this->context->currency, false),
  524. '{total_discounts}' => Tools::displayPrice($order->total_discounts, $this->context->currency, false),
  525. '{total_shipping}' => Tools::displayPrice($order->total_shipping, $this->context->currency, false),
  526. '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $this->context->currency, false));
  527. if (is_array($extra_vars))
  528. $data = array_merge($data, $extra_vars);
  529. // Join PDF invoice
  530. if ((int)Configuration::get('PS_INVOICE') && $order_status->invoice && $order->invoice_number)
  531. {
  532. $pdf = new PDF($order->getInvoicesCollection(), PDF::TEMPLATE_INVOICE, $this->context->smarty);
  533. $file_attachement['content'] = $pdf->render(false);
  534. $file_attachement['name'] = Configuration::get('PS_INVOICE_PREFIX', (int)$order->id_lang).sprintf('%06d', $order->invoice_number).'.pdf';
  535. $file_attachement['mime'] = 'application/pdf';
  536. }
  537. else
  538. $file_attachement = null;
  539. if (Validate::isEmail($this->context->customer->email))
  540. Mail::Send(
  541. (int)$order->id_lang,
  542. 'order_conf',
  543. Mail::l('Order confirmation', (int)$order->id_lang),
  544. $data,
  545. $this->context->customer->email,
  546. $this->context->customer->firstname.' '.$this->context->customer->lastname,
  547. null,
  548. null,
  549. $file_attachement,
  550. null, _PS_MAIL_DIR_, false, (int)$order->id_shop
  551. );
  552. }
  553. // updates stock in shops
  554. if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT'))
  555. {
  556. $product_list = $order->getProducts();
  557. foreach ($product_list as $product)
  558. {
  559. // if the available quantities depends on the physical stock
  560. if (StockAvailable::dependsOnStock($product['product_id']))
  561. {
  562. // synchronizes
  563. StockAvailable::synchronize($product['product_id'], $order->id_shop);
  564. }
  565. }
  566. }
  567. }
  568. else
  569. {
  570. $error = Tools::displayError('Order creation failed');
  571. Logger::addLog($error, 4, '0000002', 'Cart', intval($order->id_cart));
  572. die($error);
  573. }
  574. } // End foreach $order_detail_list
  575. // Use the last order as currentOrder
  576. $this->currentOrder = (int)$order->id;
  577. return true;
  578. }
  579. else
  580. {
  581. $error = Tools::displayError('Cart cannot be loaded or an order has already been placed using this cart');
  582. Logger::addLog($error, 4, '0000001', 'Cart', intval($this->context->cart->id));
  583. die($error);
  584. }
  585. }
  586. public function formatProductAndVoucherForEmail($content)
  587. {
  588. return '<table style="width: 100%; font-family: Verdana,sans-serif; font-size: 11px; color: #374953;">
  589. <colgroup>
  590. <col width="15%"/>
  591. <col width="30%"/>
  592. <col width="20%"/>
  593. <col width="15%"/>
  594. <col width="20%"/>
  595. </colgroup>'.$content.'</table>';
  596. }
  597. /**
  598. * @param Object Address $the_address that needs to be txt formated
  599. * @return String the txt formated address block
  600. */
  601. protected function _getTxtFormatedAddress($the_address)
  602. {
  603. $adr_fields = AddressFormat::getOrderedAddressFields($the_address->id_country, false, true);
  604. $r_values = array();
  605. foreach ($adr_fields as $fields_line)
  606. {
  607. $tmp_values = array();
  608. foreach (explode(' ', $fields_line) as $field_item)
  609. {
  610. $field_item = trim($field_item);
  611. $tmp_values[] = $the_address->{$field_item};
  612. }
  613. $r_values[] = implode(' ', $tmp_values);
  614. }
  615. $out = implode("\n", $r_values);
  616. return $out;
  617. }
  618. /**
  619. * @param Object Address $the_address that needs to be txt formated
  620. * @return String the txt formated address block
  621. */
  622. protected function _getFormatedAddress(Address $the_address, $line_sep, $fields_style = array())
  623. {
  624. return AddressFormat::generateAddress($the_address, array('avoid' => array()), $line_sep, ' ', $fields_style);
  625. }
  626. /**
  627. * @param int $id_currency : this parameter is optionnal but on 1.5 version of Prestashop, it will be REQUIRED
  628. * @return Currency
  629. */
  630. public function getCurrency($current_id_currency = null)
  631. {
  632. if (!(int)$current_id_currency)
  633. $current_id_currency = Context::getContext()->currency->id;
  634. if (!$this->currencies)
  635. return false;
  636. if ($this->currencies_mode == 'checkbox')
  637. {
  638. $currencies = Currency::getPaymentCurrencies($this->id);
  639. return $currencies;
  640. }
  641. elseif ($this->currencies_mode == 'radio')
  642. {
  643. $currencies = Currency::getPaymentCurrenciesSpecial($this->id);
  644. $currency = $currencies['id_currency'];
  645. if ($currency == -1)
  646. $id_currency = (int)$current_id_currency;
  647. elseif ($currency == -2)
  648. $id_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT');
  649. else
  650. $id_currency = $currency;
  651. }
  652. if (!isset($id_currency) || empty($id_currency))
  653. return false;
  654. return (new Currency($id_currency));
  655. }
  656. /**
  657. * Allows specified payment modules to be used by a specific currency
  658. *
  659. * @since 1.4.5
  660. * @param int $id_currency
  661. * @param array $id_module_list
  662. * @return boolean
  663. */
  664. public static function addCurrencyPermissions($id_currency, array $id_module_list = array())
  665. {
  666. $values = '';
  667. if (count($id_module_list) == 0)
  668. {
  669. // fetch all installed module ids
  670. $modules = PaymentModuleCore::getInstalledPaymentModules();
  671. foreach ($modules as $module)
  672. $id_module_list[] = $module['id_module'];
  673. }
  674. foreach ($id_module_list as $id_module)
  675. $values .= '('.(int)$id_module.','.(int)$id_currency.'),';
  676. if (!empty($values))
  677. {
  678. return Db::getInstance()->execute('
  679. INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_currency`)
  680. VALUES '.rtrim($values, ',')
  681. );
  682. }
  683. return true;
  684. }
  685. /**
  686. * List all installed and active payment modules
  687. * @see Module::getPaymentModules() if you need a list of module related to the user context
  688. *
  689. * @since 1.4.5
  690. * @return array module informations
  691. */
  692. public static function getInstalledPaymentModules()
  693. {
  694. $hook_payment = 'Payment';
  695. if (Db::getInstance()->getValue('SELECT `id_hook` FROM `'._DB_PREFIX_.'hook` WHERE `name` = \'displayPayment\''))
  696. $hook_payment = 'displayPayment';
  697. return Db::getInstance()->executeS('
  698. SELECT DISTINCT m.`id_module`, h.`id_hook`, m.`name`, hm.`position`
  699. FROM `'._DB_PREFIX_.'module` m
  700. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_module` = m.`id_module`
  701. LEFT JOIN `'._DB_PREFIX_.'hook` h ON hm.`id_hook` = h.`id_hook`
  702. WHERE h.`name` = \''.pSQL($hook_payment).'\'
  703. AND m.`active` = 1
  704. ');
  705. }
  706. public static function preCall($module_name)
  707. {
  708. if (!parent::preCall($module_name))
  709. return false;
  710. if (($module_instance = Module::getInstanceByName($module_name)))
  711. if (!$module_instance->currencies || ($module_instance->currencies && count(Currency::checkPaymentCurrencies($module_instance->id))))
  712. return true;
  713. return false;
  714. }
  715. }