<?php if (!defined('HTTP_SERVER')) die('You can not access this file directly!');
/**
  \licence GPL 2005-2010  The osCSS developers - osCSS Open Source E-commerce
  \portion code Copyright (c) 2002 osCommerce
  \package osCSS-2 <www http://www.oscss.org>
  \version 2.1.0
  \date  15/12/10, 11:33
  \author oscim <mail aurelien@oscim.fr> <www http://www.oscim.fr>
  \encode UTF-8
  \class checkout_process
  \dir includes/classes/
  \brief  Process order and holding order


  \subpage checkout_process Manual class

    Process order and holding order

	\section english (en) Process order  Process order and holding order

  	\section francais (fr) Gestion des process et des modules associé
  	Cette class assure l'ensemble des pre-enregsitrement et enregsitrement des commande dans l'epsace public.\n
  	Elle centralise l'ensemble des modifications des commandes, et leur confirmation
*/

class checkout_process
  extends AbstractAcaModule{

  /**
    string Formated order_id
  */
  public $the_order_formated;

  /**
    @a array  tableau de table
  */
  private static $tables;
  /**
    @a bool Mode (orders=true | holding_order=false)
  */
  private static $mode;

  /**
   * \fn  __construct($order_id='',$table='')
   * \brief Charge les module aca specifique au porduits
   * @param $order_id int|null
   * @param $table string|null
  */
  function __construct($order_id='',$table='') {
    global $language, $page;
    $this-> name='checkout_process';
    $this->modules=array();
    $this->list_mod=array();

    /*
      Selection Holding /orders
    */
    self::$mode=(tep_not_null($table)?false:true);

    $table=((!self::$mode)? '_'.strtoupper($table) : '');

    self::$tables=array( 'ORDERS'=>constant('TABLE'.$table.'_ORDERS'),
			  'TOTAL'=>constant('TABLE'.$table.'_ORDERS_TOTAL'),
			  'PRODUCTS'=>constant('TABLE'.$table.'_ORDERS_PRODUCTS'),
			  'ATTRIBUTES'=>constant('TABLE'.$table.'_ORDERS_PRODUCTS_ATTRIBUTES'),
			  'DOWNLOAD'=>constant('TABLE'.$table.'_ORDERS_PRODUCTS_DOWNLOAD'),
			  'HISTORY'=>constant('TABLE'.$table.'_ORDERS_STATUS_HISTORY'),
			);

	/*
	 * Load module type process
	 */
     foreach(parent::initialise_type('MODULE_CHECKOUT_PROCESS_INSTALLED','checkout_process') as $class){
      $cl[$class]=new $class ();
      $list_mod[$class] = $cl[$class]->sort_order;
      asort($list_mod);
      foreach($list_mod as $class=>$s){
    	$GLOBALS[$class] =$this->modules[$class] =$cl[$class];
      }
      unset($cl);unset($list_mod);
     }

    /*if(tep_not_null($order_id))*/ $this->the_order_id=(int)$order_id;
  }

  /**
   * \fn ret_modules($class='')
   * \brief retourne la liste des modules actifs  ou l'object de l'un d'eux
  */
  public function ret_modules($class=''){
    if(!empty($class) && isset($this->modules[$class]))  return $this->modules[$class];
    return $this->modules;
  }

  /**
   * \fn before_process()
   * \brief Chargement avant process et avant before_process payment
  */
  public function before_process(){
    global  $page;
      foreach ($this->modules as $key=>$module) {
        if (($module->enabled)  and (method_exists($module, 'before_process'))) {
          $funct='before_process';
          return $this->modules[$key]->$funct();
        }
      }
  }


  /**
   * \fn in_process_order()
   * \brief Chargement dans le process, dans le detail order avant boucle produits
  */
  public function in_process_order(){
    global  $page, $order;
      foreach ($this->modules as $key=>$module) {
        if (($module->enabled)  and (method_exists($module, 'in_process_order'))) {
          $funct='in_process_order';
          return $this->modules[$key]->$funct();
        }
      }
  }


  /**
   * \fn in_process_products()
   * \brief Chargement dans le process, dans  boucle produits
  */
  public function in_process_products(){
    global  $page;
      foreach ($this->modules as $key=>$module) {
        if (($module->enabled)  and (method_exists($module, 'in_process_products'))) {
          $funct='in_process_products';
          return $this->modules[$key]->$funct();
        }
      }
  }


  /**
   * \fn in_process_finish()
   * \brief Chargement dans le fin du process
  */
  public function in_process_finish(){
    global  $page;
      foreach ($this->modules as $key=>$module) {
        if (($module->enabled)  and (method_exists($module, 'in_process_finish'))) {
          $funct='in_process_finish';
          return $this->modules[$key]->$funct();
        }
      }
  }


  /**
   * \fn after_process()
   * \brief Chargement apres le  process et apres appel des after_process des pay_mod
  */
  public function after_process(){
    global  $page;
      foreach ($this->modules as $key=>$module) {
        if (($module->enabled)  and (method_exists($module, 'after_process'))) {
          $funct='after_process';
          return $this->modules[$key]->$funct();
        }
      }
  }

  /**
   * \fn get_the_order_formated()
   * \brief Renvoi numero commande formated
  */
  public function get_the_order_formated(){
    return (isset($this->the_order_formated) && !empty($this->the_order_formated)) ? $this->the_order_formated : $this->the_order_id;
  }

/*
  Function du process
*/


  /**
   * \fn preventDuplicate($array)
   * \brief
    Bacule pre commande vers commande
    Si a pre-commande existe (ds la session),elle est transformé en commande
    Si pas de pre-commande en session, creation de la commande
   * @param $array array
  */
  public function preventDuplicate($array){
    if (tep_session_is_registered('pre_order_id') && !isset($array['pre_order_id']) ) return $this->process_confirm($_SESSION['pre_order_id']);
    elseif(self::test_pre_order_id($array['pre_order_id'])) return $this->process_confirm($array['pre_order_id']);
    else {
      return $this->process_exe($array['order_totals'],$array['order']);
    }
  }

  /**
   * \fn test_pre_order_id($pre_order_id)
   * \brief  appelé par preventDuplicate
    		Test présence pre_order_id dans table holding_order ET n'existe pas dans order (holding_origin_id)
   * @param $pre_order_id int
  */
  private static function test_pre_order_id($pre_order_id){
    $DB=Database::getInstance();

    $res=$DB->query("Select orders_id from " . TABLE_HOLDING_ORDERS . " where orders_id = '" . (int)$pre_order_id . "' ");
    if($res->__get('numRows') ==0) return false;

    $res=$DB->query("Select orders_id from " . TABLE_ORDERS . " where holding_origin_id = '" . (int)$pre_order_id . "' ");
    if($res->__get('numRows') ==1) return false;

    return true;
  }


  public static function generat_uniq_id($pre_order_id=''){
    $pre_order_id=(!tep_not_null($pre_order_id))? $_SESSION['pre_order_id']:$pre_order_id;
    return date('Y-m-d').'-'.$pre_order_id;
  }

  /**
   * \brief Control de la chaine transmise et retoruné par service de paiement.
    LA chaine est composé de diverse info, contenu dans la pre_commande
   * @return $string false | pre_order_id
  */
  public static function check_string_return($string){
    $res=explode('|',$string);
    $DB=Database::getInstance();
    $query=$DB->query($sql="Select customers_id, date_purchased from " . TABLE_HOLDING_ORDERS . " where orders_id = '" . (int)$res[0] . "' ");
    $item=$query->fetchAssoc();
    if($item['customers_id'] != (int)$res[1] ) return false;
    if($item['date_purchased'] !=$res[2]) return false;

    $query=$DB->query("Select value, class from ".TABLE_HOLDING_ORDERS_TOTAL." Where orders_id='".(int)$res[0]."' and class='ot_total' ");
    $item=$query->fetchAssoc();

    if($item['value'] !=$res[3]) return false;

    return $res[0];
  }

  /**
   * \brief generation chaine unique transmise au service de paiement
   * @return  id_pre_commande|customers_id|date_purchased|value ot_total
  */
  public static function generat_string($id_pre_commande=''){
    global $customer_id;
    $DB=Database::getInstance();

    $pre_order_id=(!tep_not_null($id_pre_commande))? $_SESSION['pre_order_id']:$id_pre_commande;

    $generat=$pre_order_id.'|'.$customer_id.'|';

    $res=$DB->query("Select date_purchased from " . TABLE_HOLDING_ORDERS . " where orders_id = '" . (int)$pre_order_id . "' ");
    $item=$res->fetchAssoc();
    $generat .=$item['date_purchased'].'|';

    /// table orders_total
    $res=$DB->query("Select value, class from ".TABLE_HOLDING_ORDERS_TOTAL." Where orders_id='".(int)$pre_order_id."' and class='ot_total' ");
    $item=$res->fetchAssoc();
    $generat .=$item['value'];

    return $generat;
  }


  /**
   * \brief Ordananceur process
   * @param $order_totals
   * @param $order object orders|null
  */
  public function process_exe($order_totals,$order=''){
    global $customer_id, $currencies;

    $ip = tep_get_ip_address();
    $total_weight='';
    $total_cost='';
    $products_tax='';
    $products_ordered='';

    $order = (!is_object($order)? new order('', ((!self::$mode)? 'holding' : '') ) : $order );

      /**
	Process class
      */
      $the_order_id=$insert_id=$this->insert_order($customer_id, $order);

      /// unqieuemnt held_horder
      if(!self::$mode) {
	self::purgeHolding();
// 	self::preventDuplicateholding($the_order_id);
      }
      /**
	Process class
      */
      $this->insert_order_total($order_totals);

      /**
	Process class
      */
      $customer_notification =$this->insert_status_history($order);


      /**
	Before process module ACA
      */
      $this->in_process_order();

    // initialized for the email confirmation
      $subtotal = 0;
      $total_tax = 0;

      for ($i=0, $n=sizeof($order->products); $i<$n; $i++) {

	/**
	  Process class
	  Gestion du stock
	*/
	$products_stock_attributes=(self::$mode) ? $this->order_destock_product($i,$order) : '';

	/**
	  Process class
	  meilleur vente
	*/
	if(self::$mode)$this->update_product_bestseller( $order->products[$i]['id'],  $order->products[$i]['qty']);

	/**
	  Process class
	  Enregsitrement des produits
	*/
	$order_products_id =$this->insert_order_product($i,$products_stock_attributes,$order);


	//------insert customer choosen option to order--------
	$attributes_exist = '0';
	$products_ordered_attributes = '';
	if (isset($order->products[$i]['attributes'])) {
	  $attributes_exist = '1';
	  for ($j=0, $n2=sizeof($order->products[$i]['attributes']); $j<$n2; $j++) {
	    /**
	      Process class
	      recup des attribut de produits
	    */
	    $attributes_values=$this->load_db_attribut_product($i, $j, $order);

	    /**
	      Process class
	      enregistrement des attribut de produits
	    */
	    $this->insert_order_product_attribut($order_products_id,$attributes_values);


	    $products_ordered_attributes .= "\n\t" . $attributes_values['products_options_name'] . ' ' . $attributes_values['products_options_values_name'];
	  }
	}


	/**
	  Before process module ACA
	*/
	$this->in_process_products();

    //------insert customer choosen option eof ----
	$total_weight += ($order->products[$i]['qty'] * $order->products[$i]['weight']);
	$total_products_price=$currencies->display_price($order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty']) ;
	$total_tax += tep_calculate_tax($total_products_price, $products_tax) * $order->products[$i]['qty'];
	$total_cost += $total_products_price;

// 	$products_ordered .= $order->products[$i]['qty'] . ' x ' . $order->products[$i]['name'] . ' (' . $order->products[$i]['model'] . ') = ' . $currencies->display_price($order->products[$i]['final_price'], $order->products[$i]['tax'], $order->products[$i]['qty']) . $products_ordered_attributes . "\n";
      }


      /**
	  Before process module ACA
      */
      $this->in_process_finish();

    /**
      Fin pre commande
    */


    return array (  'the_order_id'=>$the_order_id,
		    'customer_notification'=>$customer_notification,
		    'total_weight'=>$total_weight,
		    'total_tax'=>$total_tax,
		    'total_cost'=>$total_cost,
		  );
  }


  /**
   * \fn process_confirm($pre_order_id)
   * \brief Ordananceur process holding vs order
   * @param $pre_order_id int  id pre commande
  */
  public function process_confirm($pre_order_id){
    $corresp=$corresp_attrib=array();

    $DB=Database::getInstance();

    /// table order
    $order_query = $DB->query("select orders_prefix, customers_id, customers_name, customers_company, customers_street_address, customers_suburb, customers_city, customers_postcode, customers_state, customers_country, customers_telephone, customers_email_address, customers_address_format_id, delivery_name, delivery_company, delivery_street_address, delivery_suburb, delivery_city, delivery_postcode, delivery_state, delivery_country, delivery_address_format_id, billing_name, billing_company, billing_street_address, billing_suburb, billing_city, billing_postcode, billing_state, billing_country, billing_address_format_id, payment_method, cc_type, cc_owner, cc_number, cc_expires, currency, currency_value, date_purchased, orders_status, last_modified, ip_address from " . TABLE_HOLDING_ORDERS . " where orders_id = '" . (int)$pre_order_id . "'");
    $order = $order_query->fetchAssoc();

    $sql_data_array = array('holding_origin_id'=>(int)$pre_order_id,
			    'orders_prefix' => tep_db_input($order['orders_prefix']),
			    'currency' => tep_db_input($order['currency']),
			    'customers_id' => (int)$order['customers_id'],
			    'currency_value' => tep_db_input($order['currency_value']),
			    'payment_method' => tep_db_input($order['payment_method']),
			    'cc_type' => tep_db_input($order['cc_type']),
			    'cc_owner' => tep_db_input($order['cc_owner']),
			    'cc_number' => tep_db_input($order['cc_number']),
			    'cc_expires' => tep_db_input($order['cc_expires']),
			    'date_purchased' => date('Y-m-d H:i:s'),
			    'orders_status' => tep_db_input($order['orders_status']),
			    'ip_address' => tep_db_input($order['ip_address']),
			    'last_modified' => tep_db_input($order['last_modified']),
			    'customers_name' => tep_db_input($order['customers_name']),
			    'customers_company' => tep_db_input($order['customers_company']),
			    'customers_street_address' => tep_db_input($order['customers_street_address']),
			    'customers_suburb' => tep_db_input($order['customers_suburb']),
			    'customers_city' => tep_db_input($order['customers_city']),
			    'customers_postcode' => tep_db_input($order['customers_postcode']),
			    'customers_state' => tep_db_input($order['customers_state']),
			    'customers_country' => tep_db_input($order['customers_country']),
			    'customers_address_format_id' => tep_db_input($order['customers_address_format_id']),
			    'customers_telephone' => tep_db_input($order['customers_telephone']),
			    'customers_email_address' => tep_db_input($order['customers_email_address']),
			    'delivery_name' => tep_db_input($order['delivery_name']),
			    'delivery_company' => tep_db_input($order['delivery_company']),
			    'delivery_street_address' => tep_db_input($order['delivery_street_address']),
			    'delivery_suburb' => tep_db_input($order['delivery_suburb']),
			    'delivery_city' => tep_db_input($order['delivery_city']),
			    'delivery_postcode' => tep_db_input($order['delivery_postcode']),
			    'delivery_state' => tep_db_input($order['delivery_state']),
			    'delivery_country' => tep_db_input($order['delivery_country']),
			    'delivery_address_format_id' => (int)$order['delivery_address_format_id'],
			    'billing_name' => tep_db_input($order['billing_name']),
			    'billing_company' => tep_db_input($order['billing_company']),
			    'billing_street_address' => tep_db_input($order['billing_street_address']),
			    'billing_suburb' => tep_db_input($order['billing_suburb']),
			    'billing_city' => tep_db_input($order['billing_city']),
			    'billing_postcode' => tep_db_input($order['billing_postcode']),
			    'billing_state' => tep_db_input($order['billing_state']),
			    'billing_country' => tep_db_input($order['billing_country']),
			    'billing_address_format_id' => (int)$order['billing_address_format_id']);

    $query=tep_db_perform(TABLE_ORDERS, $sql_data_array);

    $this->the_order_id=$order_id=$query->__get('insertId');

    /// table orders_total
    $res=$DB->query("Select title, text, value, class, sort_order from ".TABLE_HOLDING_ORDERS_TOTAL." Where orders_id='".(int)$pre_order_id."' ");

    while ($item = $res->fetchAssoc())
      $DB->query("Insert into ".TABLE_ORDERS_TOTAL." (orders_id,title,text,value,class,sort_order) values ('".(int)$order_id."','".tep_db_input($item['title'])."','".tep_db_input($item['text'])."','".tep_db_input($item['value'])."','".tep_db_input($item['class'])."','".tep_db_input($item['sort_order'])."') ");


    //! product / products_attributes / products_download
    $orders_products_query = tep_db_query("select orders_products_id, products_id,products_name, products_model, products_price, products_tax, products_quantity, final_price from " . TABLE_HOLDING_ORDERS_PRODUCTS . " where orders_id = '" . (int)$pre_order_id . "'");
    while ($orders_products = tep_db_fetch_array($orders_products_query)) {

      $sql_data_array2 = array('orders_id' => (int)$order_id,
			      'products_id' => (int)$orders_products['products_id'],
			      'products_quantity' => tep_db_input($orders_products['products_quantity']),
			      'products_name' => tep_db_input($orders_products['products_name']),
			      'products_model' => tep_db_input($orders_products['products_model']),
			      'products_tax' => tep_db_input($orders_products['products_tax']),
			      'products_price' => tep_db_input($orders_products['products_price']),
			      'final_price' => tep_db_input($orders_products['final_price']));

      $res2=tep_db_perform(TABLE_ORDERS_PRODUCTS, $sql_data_array2);
      $order_products_id = tep_db_insert_id($res2);


      $attributes_query = tep_db_query("select products_options, products_options_values, options_values_price, price_prefix from " . TABLE_HOLDING_ORDERS_PRODUCTS_ATTRIBUTES . " where orders_id = '" . (int)$pre_order_id . "' and orders_products_id = '" . (int)$orders_products['orders_products_id'] . "'");
      if (tep_db_num_rows($attributes_query)) {
	while ($attributes = tep_db_fetch_array($attributes_query)) {
	  $sql_data_array1 = array('orders_id' => (int)$order_id,
				  'orders_products_id' => (int)$order_products_id,
				  'products_options' => tep_db_input($attributes['products_options']),
				  'products_options_values' => tep_db_input($attributes['products_options_values']),
				  'price_prefix' => tep_db_input($attributes['price_prefix']),
				  'options_values_price' => tep_db_input($attributes['options_values_price']) );

	  tep_db_perform(TABLE_ORDERS_PRODUCTS_ATTRIBUTES, $sql_data_array1);
	}
      }
    }


    /// table orders_status_history
    $res=$DB->query("Select * from ".TABLE_HOLDING_ORDERS_STATUS_HISTORY." Where orders_id='".(int)$pre_order_id."' ");

    while ($item = $res->fetchAssoc())
      $DB->query("Insert into ".TABLE_ORDERS_STATUS_HISTORY." ( orders_id , orders_status_id , date_added , customer_notified , comments) values ('".$order_id."','".$item['orders_status_id']."','".$item['date_added']."','".$item['customer_notified']."','".$item['comments']."') ");



    //! purge
    self::purgeHolding($pre_order_id);

    //! emptying cart
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET . " where customers_id = '" . (int)$order['customers_id'] . "'");
    tep_db_query("delete from " . TABLE_CUSTOMERS_BASKET_ATTRIBUTES . " where customers_id = '" . (int)$order['customers_id'] . "'");


    $this->email_confirm_order($order_id);
    //! RETURN
    return array (  'the_order_id'=>$order_id,
		    'customer_notification'=>(_cst_bool('SEND_EMAILS')) ? '1' : '0',
		  );
  }

  /**
   * \fn email_confirm_order($order_id)
   * \brief Envoi email de confirmation de commande
   * @param $order_id
  */
  public function email_confirm_order($order_id){
    global $payment,$shipping,$language, $currencies ;

    $order=new order($order_id);


    if (isset($$payment) && is_object($$payment)) {
      $payment_class = $$payment;
      $payment_= $payment_class->title;
      $email_footer = (isset($payment_class->email_footer)) ? $payment_class->email_footer . "\n\n" : '';
    }
    else {
      $payment_=$order->info['payment_method'];
      $email_footer='';
    }
    if (is_array($shipping)) {
      $shipping_= $shipping['title'];
    } else {
      $shipping_=$order->info['shipping_method'];
    }

    $myarray = array(
		    'from'=>STORE_OWNER,
		    'subject'=>'',
		    '_oID'=>$this->get_the_order_formated(),
		    'date_purchased'=>tep_strftime(DATE_FORMAT_LONG),
		    '_comments'=>((isset($order->info['comments']) )?tep_db_output($order->info['comments']) : ''),
		    'sendToFormatted'=> tep_address_format($order->delivery['format_id'], $order->delivery, 1, ' ', '<br />'),
		    'billToFormatted'=>tep_address_format($order->billing['format_id'], $order->billing, 1, ' ', '<br />'),
		    'payment_'=>$payment_,
		    'shipping_'=>$shipping_,
		    'order_totals'=>$order->totals,
		    'order_products'=>$order->products,
		    'email_footer'=>$email_footer,
    //                   '_ostatus'=>$orders_status_array[$status],
		    '_oLinks'=>tep_href_link(FILENAME_ACCOUNT, 'action=history_info&amp;order_id=' . $order_id, 'SSL'),
		    'email_use_html'=>_cst_bool('EMAIL_USE_HTML'),
		    'message'=>'',
		    );


    $message_final=tep_post_prepare_email('checkout_process.tpl',$language,$myarray);

    tep_mail($order->customer['firstname'] . ' ' . $order->customer['lastname'], $order->customer['email_address'], EMAIL_TEXT_SUBJECT, $message_final, STORE_OWNER, EMAIL_FROM);

    /// admin notif
    notification::notifInMess('checkout_process','copy '.EMAIL_TEXT_SUBJECT,$message_final,'customers_id='.$order->info['customers_id']);

  }

  /**
   * \fn insert_order($customer_id, $order, $update='')
   * \brief Enregsitrement de la table order
   * @param $customer_id int  l'id
   * @param $order object
   * @param $update string si string update
    @return order_id
  */
  private function insert_order($customer_id, $order, $update=''){
    $date_purchased=date("Y-m-d H:i:s");
    $format_prefix='';
    $sql_data_array = array('customers_id' => $customer_id,
			    'orders_prefix' => $format_prefix,
                            'customers_name' => $order->customer['firstname'] . ' ' . $order->customer['lastname'],
                            'customers_company' => (string)$order->customer['company'],
                            'customers_street_address' => (string)$order->customer['street_address'],
                            'customers_suburb' => $order->customer['suburb'],
                            'customers_city' => (string)$order->customer['city'],
                            'customers_postcode' => $order->customer['postcode'],
                            'customers_state' => (string)$order->customer['state'],
                            'customers_country' => $order->customer['country']['title'],
                            'customers_telephone' => $order->customer['telephone'],
                            'customers_email_address' => $order->customer['email_address'],
                            'customers_address_format_id' => $order->customer['format_id'],
                            'delivery_name' => $order->delivery['firstname'] . ' ' . $order->delivery['lastname'],
                            'delivery_company' => $order->delivery['company'],
                            'delivery_street_address' => (string)$order->delivery['street_address'],
                            'delivery_suburb' => $order->delivery['suburb'],
                            'delivery_city' => (string)$order->delivery['city'],
                            'delivery_postcode' => $order->delivery['postcode'],
                            'delivery_state' => $order->delivery['state'],
                            'delivery_country' => $order->delivery['country']['title'],
                            'delivery_address_format_id' => $order->delivery['format_id'],
                            'billing_name' => $order->billing['firstname'] . ' ' . $order->billing['lastname'],
                            'billing_company' => $order->billing['company'],
                            'billing_street_address' => (string)$order->billing['street_address'],
                            'billing_suburb' => $order->billing['suburb'],
                            'billing_city' => (string)$order->billing['city'],
                            'billing_postcode' => $order->billing['postcode'],
                            'billing_state' => $order->billing['state'],
                            'billing_country' => $order->billing['country']['title'],
                            'billing_address_format_id' => $order->billing['format_id'],
                            'payment_method' => $order->info['payment_method'],
                            'cc_type' => $order->info['cc_type'],
                            'cc_owner' => $order->info['cc_owner'],
                            'cc_number' => substr_replace($order->info['cc_number'],"XXXXXXXX",4,8),
                            'cc_expires' => $order->info['cc_expires'],
                            'date_purchased' => $date_purchased,
                            'orders_status' => (int)$order->info['order_status'],
                            'currency' => (string)$order->info['currency'],
                            'currency_value' => $order->info['currency_value'],
                            'ip_address' => tep_get_ip_address(),
			    'total_weight'=>$order->info['total_weight']);

    if(empty($update)) tep_db_perform(self::$tables['ORDERS'], $sql_data_array);
    else tep_db_perform(self::$tables['ORDERS'], $sql_data_array, 'update', $update);

    $cs_s=tep_db_query("select orders_id from " . self::$tables['ORDERS'] . "  where customers_id='".$customer_id."' and date_purchased='".$date_purchased."'");
    $cs=tep_db_fetch_array($cs_s);
    $_SESSION['the_order_id'] =$this->the_order_id=$insert_id=$cs['orders_id'];


    /* Ajout prefix de numero de commande */
    if(defined('ORDERS_NUMBER_PREFIX') && ORDERS_NUMBER_PREFIX !='' ){
      preg_match('#([a-zA-Z]*)(.{0,1})([0-9])#',ltrim(ORDERS_NUMBER_PREFIX),$reg);
      $the_order_final=date($reg[1]).$reg[2].str_pad('',(int)($reg[3]-strlen($insert_id)),"0" ,STR_PAD_LEFT);
      $sql_data_array = array( 'orders_prefix' =>$the_order_final  );
      tep_db_perform(self::$tables['ORDERS'], $sql_data_array, 'update', " orders_id='".$insert_id."' ");
      $the_order_final .=$insert_id;
     $this->the_order_formated=$_SESSION['the_order_formated']=$the_order_final;
    }



    return  $this->the_order_id;
  }


  /**
   * \fn insert_order_total($order_totals)
   * \brief Order total
   * @param $order_totals array resultat de $order_total->process()
   * @return no
  */
  private function insert_order_total($order_totals){
    for ($i=0, $n=sizeof($order_totals); $i<$n; $i++) {
      $sql_data_array = array('orders_id' =>  $this->the_order_id,
                              'title' => tep_db_input($order_totals[$i]['title']),
                              'text' => tep_db_input($order_totals[$i]['text']),
                              'value' => tep_db_input($order_totals[$i]['value']),
                              'class' => tep_db_input($order_totals[$i]['code']),
                              'sort_order' => (int)$order_totals[$i]['sort_order']);
      tep_db_perform(self::$tables['TOTAL'], $sql_data_array);
    }
  }

  /**
   * \fn update_status_order($order_status_id)
   * \brief Mise a jour du status
   * @param $order_status_id
  */
  public function update_status_order($order_status_id){
    tep_db_query("update " . self::$tables['ORDERS'] . " set orders_status = '" . (int)$order_status_id . "', last_modified = now() where orders_id = '" .  (int)$this->the_order_id . "'");
  }


  /**
   * \fn insert_status_history($order)
   * \brief status history
   * @param $object object/array
   * @return $customer_notification bool
  */
  public function insert_status_history($order){
    $customer_notification = (_cst_bool('SEND_EMAILS')) ? '1' : '0';
    $sql_data_array = array('orders_id' =>   (int)$this->the_order_id ,
                            'orders_status_id' =>  (is_object($order)?  (int)$order->info['order_status']:  (int)$order['orders_status_id']),
                            'date_added' => 'now()',
                            'customer_notified' => (is_object($order)?  (int)$customer_notification :  (int)$order['customer_notified']) ,
                            'comments' => (is_object($order)? tep_db_input($order->info['comments']): tep_db_input($order['comments']) ) );
    tep_db_perform(self::$tables['HISTORY'], $sql_data_array);

    return $customer_notification;
  }

  /**
   * \fn insert_order_product($i,$products_stock_attributes, $order)
   * \brief In boucle product
   * @param $i int  index de boucle
   * @param $products_stock_attributes string
   * @param $order object
   * @return $order_products_id
  */
  private function insert_order_product($i,$products_stock_attributes, $order){
    $sql_data_array = array('orders_id' => $this->the_order_id,
                          'products_id' => tep_get_prid($order->products[$i]['id']),
                          'products_model' => tep_db_input($order->products[$i]['model']),
                          'products_name' => tep_db_input($order->products[$i]['name']),
                          'products_price' => tep_db_input($order->products[$i]['price']),
                          'final_price' => tep_db_input($order->products[$i]['final_price']),
                          'products_tax' => tep_db_input($order->products[$i]['tax']),
                          'products_quantity' => (int)$order->products[$i]['qty'],
                          'products_stock_attributes' => tep_db_input($products_stock_attributes) );
    tep_db_perform(self::$tables['PRODUCTS'], $sql_data_array);

    $cs_s=tep_db_query("select orders_products_id from " . self::$tables['PRODUCTS'] . "  where orders_id='".(int)$this->the_order_id."' and products_id='".tep_get_prid($order->products[$i]['id'])."' and final_price='".tep_db_input($order->products[$i]['final_price'])."' order by  orders_products_id DESC LIMIT 1 ");
    $cs=tep_db_fetch_array($cs_s);
    $order_products_id=$cs['orders_products_id'];
    return $order_products_id;
  }

  /**
   * \fn insert_order_product_attribut($order_products_id,$attributes_values)
   * \brief Insert db product attribute
   * @param int $order_products_id
   * @param aray $attributes_values
   * @return no
  */
  private function insert_order_product_attribut($order_products_id,$attributes_values){
    $sql_data_array = array('orders_id' => (int)$this->the_order_id,
                            'orders_products_id' => (int)$order_products_id,
                            'products_options' => tep_db_input($attributes_values['products_options_name']),
                            'products_options_values' => tep_db_input($attributes_values['products_options_values_name']),
                            'options_values_price' => tep_db_input($attributes_values['options_values_price']),
                            'price_prefix' => tep_db_input($attributes_values['price_prefix']));
    tep_db_perform(self::$tables['ATTRIBUTES'], $sql_data_array);

    if ((_cst_bool('DOWNLOAD_ENABLED')) && isset($attributes_values['products_attributes_filename']) && tep_not_null($attributes_values['products_attributes_filename'])) {
      $sql_data_array = array('orders_id' => (int)$this->the_order_id,
                              'orders_products_id' => (int)$order_products_id,
                              'orders_products_filename' => tep_db_input($attributes_values['products_attributes_filename']),
                              'download_maxdays' => tep_db_input($attributes_values['products_attributes_maxdays']),
                              'download_count' => tep_db_input($attributes_values['products_attributes_maxcount']));
      tep_db_perform(self::$tables['DOWNLOAD'], $sql_data_array);
    }
  }


  /**
   * \fn load_db_attribut_product($i,$j,$order)
   * \brief Load db attribute products
   * @param $i int  index boucle produit
   * @param $j int  index boucle attribute produits
   * @param $order object  object order
   * @return $attributes_values
  */
  private function load_db_attribut_product($i,$j,$order){
    global $languages_id;
    if (_cst_bool('DOWNLOAD_ENABLED')) {
      $attributes_query = "select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix, pad.products_attributes_maxdays, pad.products_attributes_maxcount , pad.products_attributes_filename
                            from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa
                            left join " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad
                            on pa.products_attributes_id=pad.products_attributes_id
                            where pa.products_id = '" . (int)$order->products[$i]['id'] . "'
                            and pa.options_id = '" . (int)$order->products[$i]['attributes'][$j]['option_id'] . "'
                            and pa.options_id = popt.products_options_id
                            and pa.options_values_id = '" . (int)$order->products[$i]['attributes'][$j]['value_id'] . "'
                            and pa.options_values_id = poval.products_options_values_id
                            and popt.language_id = '" . (int)$languages_id . "'
                            and poval.language_id = '" .(int) $languages_id . "'";
      $attributes = tep_db_query($attributes_query);
    } else {
      $attributes = tep_db_query("select popt.products_options_name, poval.products_options_values_name, pa.options_values_price, pa.price_prefix from " . TABLE_PRODUCTS_OPTIONS . " popt, " . TABLE_PRODUCTS_OPTIONS_VALUES . " poval, " . TABLE_PRODUCTS_ATTRIBUTES . " pa where pa.products_id = '" .(int) $order->products[$i]['id'] . "' and pa.options_id = '" . (int)$order->products[$i]['attributes'][$j]['option_id'] . "' and pa.options_id = popt.products_options_id and pa.options_values_id = '" .(int) $order->products[$i]['attributes'][$j]['value_id'] . "' and pa.options_values_id = poval.products_options_values_id and popt.language_id = '" .(int) $languages_id . "' and poval.language_id = '" . (int)$languages_id . "'");
    }
    $attributes_values = tep_db_fetch_array($attributes);

    /* Specifique option type texte / file champ personnalisé par le client */
    $attributes_values['products_options_values_name'] = $order->products[$i]['attributes'][$j]['value'];
    return $attributes_values;
  }


  /**
   * \fn order_destock_product($i,$order)
   * \brief Destock de produits
   * \author oscim <mail oscim@oscss.org> <www http://www.oscim.fr>
   * @param $order int  valeur de boucle
   * @param $order object
   * @return $products_stock_attributes
  */
  private function order_destock_product($i,$order){

    $products_stock_attributes=null;

    if (_cst_bool('STOCK_LIMITED')) {

      $products_attributes =(isset($order->products[$i]['attributes'])) ?  $order->products[$i]['attributes'] : array();
      if (_cst_bool('DOWNLOAD_ENABLED') ) {
        $stock_query_raw = "SELECT products_quantity, pad.products_attributes_filename
                            FROM " . TABLE_PRODUCTS . " p
                            LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES . " pa
                              ON p.products_id=pa.products_id
                            LEFT JOIN " . TABLE_PRODUCTS_ATTRIBUTES_DOWNLOAD . " pad
                              ON pa.products_attributes_id=pad.products_attributes_id
                            WHERE p.products_id = '" .tep_db_input( tep_get_prid($order->products[$i]['id']) ). "'";
        // Will work with only one option for downloadable products
        // otherwise, we have to build the query dynamically with a loop
        if (is_array($products_attributes) && isset($products_attributes[0]))
          $stock_query_raw .= " AND pa.options_id = '" . (int)$products_attributes[0]['option_id'] . "' AND pa.options_values_id = '" . (int)$products_attributes[0]['value_id'] . "'";
        $stock_query = tep_db_query($stock_query_raw);
      }
      else {
        $stock_query = tep_db_query("select products_quantity, track_stock from " . TABLE_PRODUCTS . " where products_id = '" .tep_db_input( tep_get_prid($order->products[$i]['id']) ). "'");
      }

      if (tep_db_num_rows($stock_query) > 0) {
          $stock_values = tep_db_fetch_array($stock_query);
          $actual_stock_bought = $order->products[$i]['qty'];
          $download_selected = false;

          if ((!_cst_bool('DOWNLOAD_ENABLED')) && isset($stock_values['products_attributes_filename']) && tep_not_null($stock_values['products_attributes_filename'])) {
            $download_selected = true;
            $products_stock_attributes='$$DOWNLOAD$$';
          }

	  //! Gestion stock et attribut
          //! If not downloadable and attributes present, adjust attribute stock
          if (!$download_selected  && is_array($products_attributes) && count($products_attributes)>0) {
            $all_nonstocked = true;
            $products_stock_attributes_array = array();
            foreach ($products_attributes as $attribute) {
              $products_stock_attributes_array[] = $attribute['option_id'] . "-" . $attribute['value_id'];
              if ($attribute['track_stock'] == 1)  $all_nonstocked = false;
            }
            if ($all_nonstocked) {
              $actual_stock_bought = $order->products[$i]['qty'];

            asort($products_stock_attributes_array, SORT_NUMERIC);
            $products_stock_attributes = implode(",", $products_stock_attributes_array);

            } else {
              asort($products_stock_attributes_array, SORT_NUMERIC);
              $products_stock_attributes = implode(",", $products_stock_attributes_array);
              $attributes_stock_query = tep_db_query("select products_stock_quantity from " . TABLE_PRODUCTS_STOCK . " where products_stock_attributes = '".tep_db_input($products_stock_attributes)."' AND products_id = '" .tep_db_input( tep_get_prid($order->products[$i]['id']) ). "'");

              if (tep_db_num_rows($attributes_stock_query) > 0) {
                $attributes_stock_values = tep_db_fetch_array($attributes_stock_query);
                $attributes_stock_left = $attributes_stock_values['products_stock_quantity'] - $order->products[$i]['qty'];

                tep_db_query("update " . TABLE_PRODUCTS_STOCK . " set products_stock_quantity = '" .tep_db_input( $attributes_stock_left) . "' where products_stock_attributes = '$products_stock_attributes' AND products_id = '" . tep_db_input(tep_get_prid($order->products[$i]['id'])) . "'");

                $actual_stock_bought = ($attributes_stock_left < 1) ? $attributes_stock_values['products_stock_quantity'] : $order->products[$i]['qty'];
              } else {
                $attributes_stock_left = 0 - $order->products[$i]['qty'];

                tep_db_query("insert into " . TABLE_PRODUCTS_STOCK . " (products_id, products_stock_attributes, products_stock_quantity) values ('" . tep_db_input(tep_get_prid($order->products[$i]['id'])) . "', '" .tep_db_input($products_stock_attributes) . "', '" . tep_db_input($attributes_stock_left ). "')");

                $actual_stock_bought = 0;
              }
            }
          }

	  //! stock produit sans attribut et non downloadable
	  elseif (!$download_selected && $stock_values['track_stock']>0) {
            $stock_left = $stock_values['products_quantity'] - $actual_stock_bought;

            tep_db_query($sql="update " . TABLE_PRODUCTS . " set products_quantity = '" .tep_db_input( $stock_left ). "' where products_id = '" . tep_db_input( tep_get_prid($order->products[$i]['id']) ). "'");

	    //! descative les produit qu ne sont plus en stock, si l'achat hors stock nest pas autorise
            if ( ($stock_left < 1) && (!_cst_bool('STOCK_ALLOW_CHECKOUT')) && @$stock_values['track_stock'] ==1 )
              tep_db_query("update " . TABLE_PRODUCTS . " set products_status = '0' where products_id = '" .tep_db_input( tep_get_prid($order->products[$i]['id']) ). "'");

          }

      }
    }
    return $products_stock_attributes;
  }


  /**
   * \fn purgeHolding($held_order='')
   * \brief Purge l'ordre in holding, basé sur valeur en session ou en arguement
   * @param $held_order int
  */
  public static function purgeHolding($held_order=''){
    $held_order=(!tep_not_null($held_order))? (int)@$_SESSION['pre_order_id']:(int)$held_order;
    if($held_order==0) return false;
    tep_db_query("delete from " . TABLE_HOLDING_ORDERS . " where orders_id = '" . (int)$held_order . "'");
    tep_db_query("delete from " . TABLE_HOLDING_ORDERS_PRODUCTS . " where orders_id = '" . (int)$held_order . "'");
    tep_db_query("delete from " . TABLE_HOLDING_ORDERS_PRODUCTS_ATTRIBUTES . " where orders_id = '" . (int)$held_order . "'");
    tep_db_query("delete from " . TABLE_HOLDING_ORDERS_STATUS_HISTORY . " where orders_id = '" . (int)$held_order . "'");
    tep_db_query("delete from " . TABLE_HOLDING_ORDERS_TOTAL . " where orders_id = '" . (int)$held_order . "'");

    if (tep_session_is_registered('pre_order_id')) tep_session_unregister('pre_order_id');
  }


  /**
   * \fn update_product_bestseller($pid, $qty)
   * \brief Update meilleur vente
   * @param $pid int
   * @param $qty int
  */
  private function update_product_bestseller($pid, $qty){
    tep_db_query($sql="update " . TABLE_PRODUCTS . " set products_ordered = products_ordered + " . sprintf('%d', $qty) . " where products_id = '" .tep_db_input( tep_get_prid($pid)) . "'");
  }
}
?>