<?php
/*
  Clase SuperLineaPedidoCliente
 */

class SuperCabeceraCesta extends Objeto{
	//Variables estáticas que pertenecen a la cesta
	public static $cart = NULL;
	public static $publicFieldList = array();
	public static $campos_tabla = array();
	public static $alias = '';
	public static $nombreTabla = '';
	public static $camposSelect = '';
	public static $whereSelect = '';
	
	public $codigo = 0;
	public $cliente = '';
	public $objeto_cliente = NULL;
	public $objetoCliente = NULL;
	public $hash = '';
	//Campos que no vienen de la tabla, los calculamos sobre la marcha:
	public $cantidad = 0;
    public $base = 0;
	public $importe = 0;
	public $descuento = 0;
	public $importe_sin_descuento = 0;
	public $puntos = 0;
	public $puntos_gastados = 0;
	//Pedro 11/Abril/2019 ==> Dirección y datos de contacto del usuario TODO
	public $nombre = '';
	public $email = '';
	public $direccion = '';
	public $cod_postal = '';
	public $localidad = '';
	public $provincia = '';
	public $pais = '';
	public $zona = 0;
	public $telefono = '';
	public $codigoDireccion = 0;
	//Pedro 12/Abril/2019 ==> Tipo de envío y forma de pago
	public $tipo_envio = 0;
	public $objeto_tipo_envio = NULL;
	public $forma_pago = 0;
	public $objeto_forma_pago = NULL;
	//22/Abril/2019 ==> Gastos de envío
	public $g_envio = 0;
	//Pedro 16/Abril/2019 ==> Esta variable actúa a modo de flag indicando que estamos editando la direccion actual:
	private $editando = false;
	//Pedro 24/Abril/2019 ==> Esta variable actúa a modo de flag indicando que estamos Creando una nueva dirección
	private $creando = false;
	//Pedro 30/Abril/2019 ==> Código promocional utilizado
	public $codigo_promocional = '';
	public $objeto_codigo_promocional = NULL;
	//Pedro 15/Mayo/2019 ==> Controlamos el estado del pedido. con esta variable:
	public $ready = 0;
	//Número de pedido (para las pasarelas)	Pedro 03/Junio2019
	public $numero_pedido = '';
	public $numeros_pedidos_reservados = '';	//Pedro 30/Septiembre/2020 ==> Aquí guardamos todos los números de pedido reservados 
	private $numero_pedido_cargado = 0;		//Pedro 30/Septiembre/2020 ==> Si nos pasan un número de pedido para cargar el pedido, lo retenemos aquí para usarlo como número de pedido
	//Líneas del pedido
	public $lineas = array();
	private $lotes = array();	//Aquí vamos a guardar los lotes asodiados a las líneas de la cesta para no tener que cargar de la BD por cada línea. El índice del array es el código del lote y el valor es el precio del lote
		//Esto lo vamos a hacer sólo para los lotes que tengan un precio especial que sumar a la venta
	
	public static function getCampos($soloPrincipal = NULL){
		static::$nombreTabla = strtolower(get_called_class());
		static::getTablaRelacionada();
		//static::$campos_tabla = BaseDatos::$dictionary->{static::$nombreTabla . 'Campos'}; //==> Comento esta línea porque creo que no sirve pa na
		//Una vez que tenemos el nombre de la tabla, vamos a obtener los campos de la misma y los nombres para la selec
		if (static::$nombreTabla != ''){
			$campos = ' ';
			if ($soloPrincipal || $soloPrincipal === NULL){
				//$campos .= BaseDatos::dameCamposAlias(BaseDatos::$dictionary->{static::$nombreTabla . 'Campos'}, static::getAlias());
				$campos .= BaseDatos::dameCamposAlias(static::$nombreTabla);
			}
			if ($soloPrincipal === false || $soloPrincipal === NULL){
				$campos .= ( (trim($campos) != ' ') ? ', ' : '' ) . 
				LineaCesta::getCampos() . ', ' . 
				Usuario::getCampos() . ', ' . 
				BaseDatos::dameCamposAlias('tmp_cabecera_pedidos_direccion') . ', ' .
				BaseDatos::dameCamposAlias('formas_de_pago');
			}
			if ($soloPrincipal == NULL){
				static::$camposSelect = $campos;
			}
		}
		return($campos);
	}
	
	public static function getNombreTabla($soloPrincipal = NULL){
		//Incluimos los globals para coger el valor del idioma:
		//include('comunes/globals.php');
		static::$nombreTabla = strtolower(get_called_class());
		static::getTablaRelacionada();
		//Ahora vamos a componer el nombre de la tabla teniendo en cuenta si es sólo principal, si no o si es todo (NULL)
		$consulta = ' ';
		if ($soloPrincipal || $soloPrincipal === NULL){
			$consulta .= static::$nombreTabla . ' as `' .  static::getAlias() . '` ';
		}
		if ($soloPrincipal === false || $soloPrincipal === NULL){
			$consulta .= static::getJoin('CabeceraCesta', 'cliente', 'Usuario', 'codigo');		//Cogemos el cliente
			$consulta .= static::getJoin('CabeceraCesta', 'codigo', 'LineaCesta', 'cabecera');	//Queremos esta tabla con la de artículo
			$consulta .= static::getJoin('CabeceraCesta', 'codigo', 'tmp_cabecera_pedidos_direccion', 'tmp_cabecera_pedidos');	//Queremos esta tabla con la de artículo
			$consulta .= static::getJoin('CabeceraCesta', 'forma_pago', 'formas_de_pago', 'codigo');	//Formas de pago
			$consulta .= LineaCesta::getNombreTabla(false);										//Cogemos los left join del línea de la cesta
		}
		$consulta .= ' ';
		//Sólo asignamos el nombre de la tabla si no nos piden más parámetros
		if ($soloPrincipal == NULL){
			static::$nombreTabla = $consulta;
		}
		return($consulta);
	}
	
	public static function getWhereTabla(){
		$argumentos = func_get_args();
		$cliente = isset($argumentos[0]) ? $argumentos[0] : NULL;
		$codigo = isset($argumentos[1]) ? $argumentos[1] : 0;
		$hash = isset($argumentos[2]) ? $argumentos[2] : '';
		$limitar = isset($argumentos[3]) ? $argumentos[3] : true;
		$numero_pedido = isset($argumentos[4]) ? $argumentos[4] : 0;
		//include('comunes/globals.php');
		static::$whereSelect = '';
		if ($cliente != NULL) {
			static::$whereSelect = ' `' . static::getAlias() . '`.`cliente` = \'' . $cliente . '\' ';
		}
		if ($codigo > 0) {
			static::$whereSelect .= (static::$whereSelect != '') ? ' AND ' : '';
			static::$whereSelect .= ' `' .  static::getAlias() . '`.`codigo` = "' . $codigo . '" ';
		}
		if ($hash != '') {
			//Si ya tengo un cliente, entonces hago un OR, si no un AND:
			if (static::$whereSelect != ''){
				if ($cliente != NULL) {
					static::$whereSelect .= (static::$whereSelect != '') ? ' OR ' : '';
				}else{
					static::$whereSelect .= (static::$whereSelect != '') ? ' AND ' : '';
				}
			}
			static::$whereSelect .= ' `' .  static::getAlias() . '`.`hash` = "' . $hash . '" ';
		}
		//Pedro 04/Junio/2019 ==> se puede cargar la cesta utilizando el número de pedido que es diferente del código de la cesta, este número es para las pasarelas
		if ($numero_pedido > 0){
			static::$whereSelect .= (static::$whereSelect != '') ? ' AND ' : '';
			static::$whereSelect = ' `' . static::getAlias() . '`.`numero_pedido` LIKE "%|' . $numero_pedido . '|%" ';
		}
		
		if ($limitar){
			static::$whereSelect .= ' LIMIT 1 ';
		}
		return(static::$whereSelect);
	}
	
    function __construct($opciones = NULL) {
		//FileSystem::log("Se va a crear un objeto cesta");
		//FileSystem::log('Llamada al constructor de la cesta');
		//08/Mayo/2019 ==> Hay que inicualizar los campos en la lista de campos de la tabla sólo si no está inicializada:
		if (empty(static::$publicFieldList)){
			static::initPublicFields();
		}
		//var_dump($opciones);
		//echo("---------------------------------------------\n");
		//TODO ==> Es interesante que siempre, después de esto llamemos a una función que quite del fieldList aquellos campos que no queremos que sean modificados 
		$opciones = new ObOptions($opciones);
		//var_dump($opciones);
		$filaCabecera = $opciones->getDefaultValue('filaCabecera', NULL);
		$codigoPedido = $opciones->getDefaultValue('codigoPedido', NULL);
		$objeto_cliente = $opciones->getDefaultValue('objeto_cliente', NULL);	//Si no nos pasan cliente, queremos que la carga se haga por sesion SIEMPRE
		$cargarLineas = $opciones->getDefaultValue('cargarLineas', true);
		$codigoCliente = $opciones->getDefaultValue('codigoCliente', '');	//El código de cliente se le pasa para hacer la carga, no hay que cogerlo de la sesión para que la cesta se cargue por hash
		$numero_pedido = $opciones->getDefaultValue('numero_pedido', 0);	//Pedro 04/Junio/2019 El número de pedido, este campo se usa en las pasarelas de pago para identificar el pedido sin usar el código
		//Pedro 30/Septiembre/2020 Si nos pasan un número de pedido lo guardamos como tal porque el pedido puede tener varios números
		if ($numero_pedido > 0){
			$this->numero_pedido_cargado = $numero_pedido;
		}
		$crearSiNoExiste = $opciones->getDefaultValue('crearSiNoExiste', true);	//Pedro 11/Abril/2019 ==> Este flag crea una cesta si al cargarla no ex
		$fusionarCesta = $opciones->getDefaultValue('fusionarCesta', true);	//Esto hace que esta cesta compruebe el usuario logueado para realizar la fusión de productos
		$recalcularCesta = false;

		//Al crear este objeto puede ser la cesta asociada a la sesión (setStaticCart = false) o ser una cesta más
		$setStaticCart = false;
		
		//$fila = NULL;
		if ($filaCabecera != NULL){
			$fila = $filaCabecera;
		}else{
			//echo("El número de pedido es el: $numero_pedido \n");
			//Pedro 09/Abril/2019 ==> A partir de ahora la cesta se carga (después de por código) por hash. Así no se duplicará. En caso de estar logueado y cargar una cesta
				// que no tiene cliente, lo asignamos, guardamos y fusionamos con lo que hubiera antes
			$sentencia_sql = 'SELECT ' . static::getCampos() . ' 
									FROM ' . static::getNombreTabla() . ' ' ;
			$sentencia_where = '';
			if (intval($codigoPedido) > 0) {	// ==> Si tengo un código de pedido lo cargo (no suele ser lo normal)
				//Preparamos el FROM con las uniones correspondientes
				$sentencia_where = ' WHERE ' . static::getWhereTabla(NULL, $codigoPedido, '', false); //Cargamos todas las líneas del pedido
			}else if ($codigoCliente != '' || ($objeto_cliente != NULL && $objeto_cliente->codigo != '') ){
				if ($codigoCliente == ''){
					$codigoCliente = $objeto_cliente->codigo;
				}
				$sentencia_where = ' WHERE ' . static::getWhereTabla($codigoCliente, NULL, '', false); //Cargamos todas las líneas del pedido
			}else if ($numero_pedido > 0){
				//echo("Hay número de pedido\n");
				$sentencia_where = ' WHERE ' . static::getWhereTabla(NULL, NULL, NULL, false, $numero_pedido); //Cargamos todas las líneas del pedido
			}else{
				//En estos casos siempre se carga la cesta por hash, por tanto estamos con la estática
				$setStaticCart = true;
				//Vamos a cargar la cesta actual con el hash de la sesión:
				$sentencia_where = ' WHERE ' . static::getWhereTabla(NULL, NULL, $hash = Sesion::getHashSesion(), false);	//Estamos cargando la cesta con líneas
			}
			
			if ($sentencia_where != ''){
				$sentencia_sql = $sentencia_sql . $sentencia_where . '; ';
				//FileSystem::log("Sentencia sql\n{$sentencia_sql}\n");
				$bd = new BaseDatos();
				if ($bd->isConectado()){
					$bd->setConsultaSQL($sentencia_sql);
					$fila = $bd->getFila();
					ob_start();
					//var_dump($fila);
					$dato = ob_get_clean();
					//FileSystem::log("Cargamos: {$dato}");
				}
				
			}
		}
		//FileSystem::log('Tenemos una select con la que cogemos la fila:');
		//FileSystem::log($sentencia_sql);
		//echo("Toca cargar datos:\n");
		if ( $fila != NULL ){	// ==> Si hay que crearlo y/o tenemos fila entonces pasamos por aquí, en caso contrario hay que tener fila por narices
			//echo("Tenemos fila para cargar la cesta\n");
			//Para evitar muchos problemas de recursividad, desde este momento vamos a asignar la cesta a la variable estática
			if ($crearSiNoExiste){
				if (static::$cart != $this){
					$recalcularCesta = true;
					static::$cart = $this;
				}
			}

			//Extraemos los datos de la carga realizada.
			//FileSystem::log('Se extraen los datos de la BD');
			$this->extractData($fila);

			//FileSystem::log("Tras extraer dicho objeto nos encontramos con el código {$this->codigo}");
			//Vamos a ver si las líneas hay que cargarlas o vienen en la lista:
			$codigoLineaPedido = $this->getProp($fila, 'codigo', LineaCesta::getAlias());
			//Si el código es nulo, no nos lo han pasado y hay que cargarlo:
			//echo("Cargamos lineas?\n");
			if ($cargarLineas){
				//echo("Cargamos las líneas\n");
				if ($codigoLineaPedido == NULL){
					//echo("Cargamos las líneas pasando la cesta\n");
					//hay que cargar las líneas:
					//$this->lineas = LineaCesta::getList($this->codigo); //Paso el código, pero podría haber pasado el objeto pedido
					$this->lineas = LineaCesta::getList($this); //Paso el objeto, pero podría haber pasado el código
				}else{
					//echo("Se cargan las líneas con la select recibida\n");
					//tenemos las líneas cargadas en esta misma select:
					do{
						$tmp_linea = new LineaCesta(
							new OpcionesWidget(array(
								'filaLineaPedido' => $fila,
								'objetoPedido' => $this	// No es necesario, $fila ya lleva la cesta, pero esto es más rápido pues el objeto ya está creado
							)));
						$this->lineas[$tmp_linea->codigo] = $tmp_linea;				
					}while ($fila = $bd->getFila());
				}
				//con las líneas cargadas vamos a cargar los lotes:
				foreach($this->lineas as $clave => $linea){
					if ($linea->codigo_lote > 0){
						$loteTmp = new Lote($linea->codigo_lote);
						$this->addLote($loteTmp);
						$linea->lote = $loteTmp;
					}
				}
			}else{
				//echo("No tenemos que cargar las líneas\n");
			}

		}else{
			//Else de control

		}
		//FileSystem::log('Carga inicial de la cesta hecha');
		//$cargarLineas = false;
		//Pedro 06/Mayo/2019 ==> No hay que datos para esta cesta, pero vamo sa ver si hay una cesta para este usuario, en cuyo caso hay que fusionar:
		//Pedro 25/Septiembre/2019 ==> Modificamos este proceso para que permita la cesta en varios equipos al mismo tiempo sin estar fusionando continuamente
		if ($cargarLineas){
			//Pedro 09/Mayo/2019 ==> Esto antes se hacía en la función anterior, pero si no había líneas, no se fusionaba la cesta. 
			//Hacemos un diagrama de flujo y llegamos a la conclusión de que la mejor manera de hacer esto es la siguiente:
			//No tengo código de usuario y hay un usuario logueado ==> Cargo la cesta del usuario logueado ==> mi código y el de la cesta del usuario logueado son 
			//	distintos ==> Funsionamos
			//Vamos a tener en cuenta que haya un usuario logueado y que esta cesta aún no tenga el cliente asignado:
			//FileSystem::log('Cogemos al usuario');
			$objeto_cliente = Usuario::logueado();
			//FileSystem::log('Usuario cargado');
			if ( ($fusionarCesta ) && ($this->cliente == '') && ($objeto_cliente != NULL) ){	//Separamos esta condición en "que haya cliente" y después que haya que fusionar
				/*ob_start();
				var_dump($objeto_cliente);
				$datosCliente = ob_get_clean();
				//FileSystem::log("Es necesario fusionar las cestas cliente:" . $this->cliente . "\nDatos: " . $datosCliente);*/
				//Por hash tengo una cesta sin cliente o directamente aún no tengo cesta
				//Lo que vamos a mirar aquí es que el usuario
				//CUando pasa por aquí es porque la cesta no tiene cliente pero en la sesión si lo hay. Ahora vamos a fusionar cestas si hay una cesta anterior:
					//Esto quiere decir que se ha hecho un login o esta es la primera carga de la cesta
				if ($objeto_cliente->codigo != ''){
					//FileSystem::log("El objeto cliente que he cargado de la sesión tiene código {$objeto_cliente->codigo}.");
					$cesta_old = new CabeceraCesta(array('objeto_cliente' => $objeto_cliente, 'crearSiNoExiste' => false, 'fusionarCesta' => false));	//Cesta vieja cargada con el código de cliente
					//FileSystem::log("La cesta vieja es \n{$contenido_cesta}");
					//Modificado por pedro el 24/Septiembre/2109 ==> este if era doble, lo separamos porque hay sentencias que no tienen las mismas características:
					//Si el código de la cesta vieja y de la actual son diferentes hay que actuar:
					//FileSystem::log("{$this->codigo} != {$cesta_old->codigo}");
					if ($this->codigo != $cesta_old->codigo){
						if (!empty($cesta_old->lineas)){
							//FileSystem::log("La cesta vieja tiene líneas");
							//Hacemos la fusión, es el momento de asegurarnos de tener un código SIEMPRE:
							$this->setCode();

							//Pedro 30/Septiembre/2020 ==> La cesta antigua puede tener un número de pedido, tenemos que traérnoslo y sumarlo a la actual:
							$this->numeros_pedidos_reservados = $cesta_old->numeros_pedidos_reservados . $this->numeros_pedidos_reservados;
							//Guardamos los números reservados:
							$this->incrementarNumeroPedido($this->numeros_pedidos_reservados);

							//Fusionamos:
							foreach($cesta_old->lineas as $codigo_linea_old => $linea_old){
								//Resulta que la select de esta cesta provoca una línea vacía porque ya hay una cabecera de pedido con el cliente asignado (se ha provocado arriba al pulsar
								//Vamos a buscar en este carrito esta línea:
								$lote = $linea_old->lote;
								$oferta = $linea_old->oferta;
								$puntos_gastados = $linea_old->puntos_gastados;
								$special_id = $linea_old->special_id;
								$nuevaCantidad = $linea_old->cantidad;
								$linea_actual = $this->findProduct($linea_old->objeto_articulo, array(
									'lote' => $lote,
									'oferta' => $oferta,
									'puntos_gastados' => $puntos_gastados,
									'special_id' => $special_id
								));
								if ($linea_actual != NULL){
									//El producto está en la cesta actual, vamos a incrementarlo:
									$nuevaCantidad += $linea_actual->cantidad;
								}else if ($oferta != NULL){
									//Modificado por Pedro el 24/Septiembre/2019 ==> Cuando cambiamos de ordenador con la misma sesión, la cantidad se vuelve a multiplicar por la de la oferta
										//y con eso va creciendo la cantidad sin sentido:
										$nuevaCantidad = $nuevaCantidad / $oferta->cantidad;
								}
								$this->addProduct(array(
									'objetoArticulo' => $linea_old->objeto_articulo,
									'linea' => $linea_actual,
									'lote' => $lote,
									'oferta' => $oferta,
									'puntos_gastados' => $puntos_gastados,
									'cantidad' => $nuevaCantidad
								));
								//Hecho esto, tenemos que meter la línea en la cesta si no estába metida
							}
						}
						//Pedro 25/Septiembre/2019 ==> Hemos separado el if de arriaba en dos, la comparación de los códigos y la comprobación de si hay líneas
						//Si los códigos son diferentes limpiamos la cesta vieja, aunque no hubiese líneas
						//FileSystem::log("Borramos la cesta vieja");
						$cesta_old->deleteCart();
					}
				}
				//Modificado por Pedro el 10/Octubre/2019 ==> Asignamos el cliente:
				//Re-Modificado por Pedro el 15/Octubre/2019 ==> Se asignaba SIEMPRE el cliente a la cesta porque esto se lanzaba siempre
					//Lo lanzamos sólo cuando no hay cliente asignado pero tenemos un cliente logueado
				//Lo último es guardar esta cesta después de la fusión
				//FileSystem::log("Asignamos al cliente");
				$this->setCustomer(array('objetoCliente' => $objeto_cliente));
				$recalcularCesta = true;
			}			
		}
	
		//Ahora calculamos importes y demás datos de la cesta en caso de que esta cesta no sea la estática:
		if ($recalcularCesta){
			//FileSystem::log('Recalculamos');
			$this->recalculate();
		}
		
		//Pedro 10/Mayo/209 ==> Vamos a asegurarnos de que la cesta estática tenga SIEMPRE el congtenido correcto
		if ($crearSiNoExiste || $setStaticCart){
			//FileSystem::log('Asignamos cesta estática');
			static::$cart = $this;
		}
		
		
		//Pedro 10/Octubre/2019 ¿Hemos cargado la cesta por número de pedido? En ese caso esta cesta viene de una pasarela o entidad externa
			//Vamos a asignar la sesión actual a la que hay en la cesta que estamos cargando:
		if ($numero_pedido != ''){
			$hashCesta = $this->hash;
			$hashSesion = Sesion::getHashSesion();
			//Fusionamos la sesión de esta cesta con la que tenemos ahora, eliminando la actual:
			if ($hashCesta != $hashSesion){
				//FileSystem::log('Fusionamos las sesiones');
				Sesion::fuseSesions($hashCesta);
				//FileSystem::log('Sesiones fusionadas');
				//Cargamos el cliente actual:
				$usuario = Usuario::logueado();
				//Y lo metemos en la cesta:
				$this->cliente = ($usuario != NULL) ? $usuario->codigo : '';
				$this->objeto_cliente = $usuario;
				$this->objetoCliente = $usuario;			
			}
		}
		//Pedro 26/Mayo/2020 ==> El tipo de envío se cargará al final, siempre y cuando no se haya cargado ya
		if ( ($this->objeto_tipo_envio == NULL) && ($this->tipo_envio > 0) ){
			$this->setShippingType($this->tipo_envio);
		}
		//FileSystem::log('Fin de la carga');
    }
	public function extractData($fila){
		$this->codigo = (int) $this->getProp($fila, 'codigo');
		$this->cliente = trim($this->getProp($fila, 'cliente'));
		//FileSystem::log('El cliente es: ' . $this->cliente);
		if ($this->cliente != ''){
			$this->objetoCliente = $this->objeto_cliente = new Usuario(array('filaUsuario' => $fila));
			//FileSystem::log('Deberíamos tener un objeto cliente');
		}
		$this->hash = $this->getProp($fila, 'hash');
		$this->nombre = $this->getProp($fila, 'nombre', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->email = $this->getProp($fila, 'email', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->direccion = $this->getProp($fila, 'direccion', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		//FileSystem::log('Dirección cargada ' . $this->direccion);
		$this->cod_postal =  $this->getProp($fila, 'cod_postal', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->localidad = $this->getProp($fila, 'localidad', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->provincia = $this->getProp($fila, 'provincia', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->pais =  $this->getProp($fila, 'pais', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->zona = (int) $this->getProp($fila, 'zona', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->telefono = $this->getProp($fila, 'telefono', BaseDatos::dameAlias('tmp_cabecera_pedidos_direccion'));
		$this->tipo_envio = (int) $this->getProp($fila, 'tipo_envio');
		//Pedro 26/Mayo/2020 EL tipo de envío se carga de manera directa, no se utiliza una select, necesita que tengamos los datos del importe del pedido y de la localidad, etc.
		//$this->objeto_tipo_envio = new TipoEnvioCliente(array('filaTipoEnvio' => $fila));
		
		/*ob_start();
		echo("---------------------------------\n");
		var_dump($this->objeto_tipo_envio);
		$txt = ob_get_clean();
		//FileSystem::log($txt);*/
		$this->forma_pago = (int) $this->getProp($fila, 'forma_pago');
		$this->objeto_forma_pago = new FormasPago(array('filaFormaPago' => $fila));
		$this->codigoDireccion = (int) $this->getProp($fila, 'direccion_envio');
		$this->codigo_promocional = $this->getProp($fila, 'codigo_promocional');
		$this->numero_pedido = $this->getProp($fila, 'numero_pedido');
		//Pedro 30/Septiembre/2020 ==> A partir de ahora el número de pedido es una cadena con todos los números que te hayan asignado para que no haya problemas  cuando un cliente navegue en varias pestañas
		$this->numeros_pedidos_reservados = $this->numero_pedido;
		//Pedro 30/Septiembre/2020 ==> Si cargamos el pedido por número, se lo asignamos desde el principio
		if ($this->numero_pedido_cargado > 0){
			$this->numero_pedido = $this->numero_pedido_cargado;
		}else{
            $this->numero_pedido = explode('||', $this->numero_pedido);
            if (is_array($this->numero_pedido)) {
                $this->numero_pedido = $this->numero_pedido[array_key_last($this->numero_pedido)];
                $this->numero_pedido = str_replace('|', '', $this->numero_pedido);
            } else {
                $this->numero_pedido = '';
            }
            if ($this->codigo_promocional != '') {
                $this->objeto_codigo_promocional = new CodigosPromocionales(array('codigo_promocional' => $this->codigo_promocional));
            }
        }
	}
	
	public static function initPublicFields(){
		//En esta función se deben de poner los nombres de los campos en la tabla
		//Sólo pondremos aquellos campos a los que les vamos a dar acceso al usuario
		$publicFieldList = array();
		$publicFieldList[] = 'codigo';
		$publicFieldList[] = 'cliente';
		$publicFieldList[] = 'hash';
		$publicFieldList[] = 'direccion_envio';
		$publicFieldList[] = 'tipo_envio';
		$publicFieldList[] = 'forma_pago';
		$publicFieldList[] = 'codigo_promocional';
		$publicFieldList[] = 'numero_pedido';

		static::$publicFieldList = $publicFieldList;
	}
    function __destruct() {
        
    }

	public static function getCart($createIfNull = true){
		if (static::$cart == NULL){
			if ($createIfNull){
				static::$cart = new CabeceraCesta();	//Cargando así la cesta se mete sóla en el objeto estático
			}
		}else{
			$cart = static::$cart;
			$objeto_cliente =  Usuario::logueado();
			//Tenemos una cesta estática el problema es que podemos haberla cargado ANTES que otros datos como el cliente y por tanto puede estar incompleta.
				//Aquí vamos a chequear el cliente y hacer recálculos si procede:
			if ($cart->objeto_cliente != $objeto_cliente){
				$cart->setCustomer(array('objetoCliente' => $objeto_cliente));
				$cart->recalculate();
			}
			static::$cart = $cart;
		}
		return(static::$cart);
	}
	
	
	public static function remoteAddProductList($data){
		$retorno = new stdClass();
		$retorno->resultado = array();
		//En data tenemos los datos a añadir a la línea (producto, cantidad, lote, etc.), pero en un array
		//Vamos a ver si en "data" tenemos un EAN:
		//Si data es un string, hay que des-serializarlo (viene en json):
		if (is_string($data)){
			$data = json_decode($data);
		}
		//$data == Tiene que ser un array:
		if (isset($data->lineas_lote)){
			$elementos = $data->lineas_lote;
			if (is_array($elementos)){
				foreach($elementos as $clave => $elemento){
					$elemento->recalcular = false;
					$retorno->resultado[] = json_decode(static::remoteAddProduct($elemento));
				}
			}
		}
		ob_start();
		//Hay que recalcular la cesta:
		static::recalculateCart();
		Sesion::commit();
		$texto = ob_get_clean();
		$retorno->resultado[] = $texto;
		//Aquí tenemos que dar un retorno:
		$retorno = json_encode($retorno);
		return($retorno);
	}

	public static function remoteAddProduct($data){
		ob_start();
		//var_dump($data);
		//En data tenemos los datos a añadir a la línea (producto, cantidad, lote, etc.)
		//Vamos a ver si en "data" tenemos un EAN:
		//Si data es un string, hay que des-serializarlo (viene en json):
		if (is_string($data)){
			$data = json_decode($data);
		}
		//var_dump($data);
		$objeto_articulo = $linea_lote = NULL;
		$special_id = $ean = $accion = '';
		$cantidad = $lote = $oferta = 0;
		$retorno = new stdClass();
		if (isset($data->codigo_articulo)){
			$ean = $data->codigo_articulo;
		}else if (isset($data->ean)){
			$ean = $data->ean;
		}
		$recalcular = true;
		if (isset($data->recalcular)){
			$recalcular = $data->recalcular;
		}
		//Vamos a coger el resto de datos:
		$cantidad = (isset($data->cantidad))? $data->cantidad : NULL;
		$accion = (isset($data->accion))? $data->accion : NULL;
		$lote = (isset($data->lote))? $data->lote : NULL;
		$oferta = (isset($data->oferta))? $data->oferta : NULL;
		$puntos_gastados = (isset($data->puntos_gastados))? $data->puntos_gastados : NULL;
		$special_id = (isset($data->special_id))? $data->special_id : '';
		//Si tenemos un código de lote, necesitamos encontrrar el un artículo dentro y cargar su línea:
		if ($lote > 0){
			$objeto_lote = new Lote(intval($lote));	//	Esto hay que hacerlo así porque siempre vamos a recibir un código numérico
			$linea_lote = $objeto_lote->findProduct($ean);
		}
		if ($linea_lote != NULL){
			//Esto son trazas
			//echo("Vamos a crear un artículo de una línea de lote " . $linea_lote->codigo);
		}
		//Vamos a componer el artículo a cargar:
		$opciones = new OpcionesWidget(
			array(
				'ean_articulo' => $ean,
				'linea_lote' => $linea_lote,
				'cantidad' => $cantidad
			)
		);		
		if ($ean != ''){
			$objeto_articulo = new Articulo($opciones);
		}
		//Si hemos podido cargar el artículo vamos a seguir:
		if (is_a($objeto_articulo, "Articulo")){
			//Vamos a cargar la sesión:
			Sesion::init();
			//Vamos a cargar la cesta:
			$cart = static::getCart();
			//Es importante comprobar que exista una cabecera:
			$cart->setCode();
			//$linea = static::findInCart($objeto_articulo, array('lote' => $lote, 'oferta' => $oferta, 'puntos_gastados' => $puntos_gastados, 'special_id' => $special_id));
			//PEdro 19/Julio/2019 ==> Vamos a cargar de la cesta y no de la estática:
			$linea = $cart->findProduct($objeto_articulo, array('lote' => $lote, 'oferta' => $oferta, 'puntos_gastados' => $puntos_gastados, 'special_id' => $special_id));
			//Si la acción es restar, nos vamos a asegurar que siempre sea "sumar"
			if ($accion == 'restar'){
				$accion = 'sumar';
				$cantidad *= -1;
			}
			//Si no hay línea hay que crearla siempre que vayamos a insertar algo:
			if ( ($linea == NULL) && ($cantidad > 0) ){
				//Ahora hay que crear una línea:
				$linea = new LineaCesta(new OpcionesWidget(array('objetoPedido' => $cart, 'objetoArticulo' => $objeto_articulo)));
			}
			//echo("¿Hay línea?\n");
			//Vamos a corregir la acción, en primer siempre se suma, nunca se resta:
			if ($linea != NULL){
				//Vamos a guardar la línea en la última borrada:
				Sesion::set('lastDeleted', clone $linea);
				
				//Ahora vamos a tener en cuenta si lo que nos pasan es absoluto o no:
				if ($accion == 'absoluto'){
					$nuevaCantidad = $cantidad;
				}else{
					//Cuando sumamos, tenemos que tener en cuenta la cantidad del lote y/o de la oferta:
					if ($linea->codigo_oferta > 0){
						//La cantidad de la línea está multiplicada por la cantidad de la oferta:
						$nuevaCantidad = ($linea->cantidad/$linea->oferta->cantidad) + $cantidad;
					}else if ($linea->codigo_oferta > 0){
						$nuevaCantidad = $linea->cantidad + $cantidad;
					}else{					
						$nuevaCantidad = $linea->cantidad + $cantidad;
					}
				}
				//Vamos a controlar la acción y la nueva cantidad:
				//Ya tenemos la nueva cantidad a asignar, vamos a añadirla a la línea y a guardarla, pero ojo, que si es 0 o menor tenemos que borrar la línea:
				if ($nuevaCantidad <= 0){
					$nuevaCantidad = 0;
				}
				//echo("La nueva cantidad es: $nuevaCantidad\n");
				//Para que TODO se haga por el mismo proceso, llamamos al addProduct de la cesta:
				$cart->addProduct(array(
					'objetoArticulo' => $objeto_articulo,
					'linea' => $linea,
					'lote' => $lote,
					'oferta' => $oferta,
					'puntos_gastados' => $puntos_gastados,
					'cantidad' => $nuevaCantidad,
					'special_id' => $special_id
				));
				//var_dump($cart->lineas);
				//Recalculamos los importes y las cantidades del carrito:
				if ($recalcular){
					$cart->recalculate();
					//Por último hacemos un commit de la sesión para que se reflejen los cambios sobre el último borrado y otras cosas así:
					Sesion::commit();
				}
				//En las funciones remotas estamos trabajando con la cesta, nos aseguramos de que coincidan
			}
		}
		
		$retorno->response = ob_get_clean();
		
		//Aquí tenemos que dar un retorno:
		$retorno = json_encode($retorno);
		return($retorno);
	}
	
	public function addProduct($opciones){
		//Extraemos los datos de "options"
		$opciones = new ObOptions($opciones);
		$objetoArticulo = $opciones->getDefaultValue('objetoArticulo', NULL);
		$linea = $opciones->getDefaultValue('linea', NULL);
		$lote = $opciones->getDefaultValue('lote', 0);
		$oferta = $opciones->getDefaultValue('oferta', 0);
		$puntos_gastados = $opciones->getDefaultValue('puntos_gastados', 0);
		$cantidad = $opciones->getDefaultValue('cantidad', 0);
		$special_id = $opciones->getDefaultValue('special_id', '');
		//Es básico que la cesta tenga código para añadirle un producto
		if ($cantidad > 0){
			$this->setCode();
		}
		
		//Si no nos han pasado línea, la buscamos en esta cesta
		if($linea == NULL){
			//$linea = static::findInCart($objeto_articulo, array('lote' => $lote, 'oferta' => $oferta, 'puntos_gastados' => $puntos_gastados));
			$linea = $this->findProduct($objetoArticulo, array(
				'lote' => $lote,
				'oferta' => $oferta,
				'puntos_gastados' => $puntos_gastados,
				'special_id' => $special_id
			));
		}
		//Si no ha y línea es pq es nueva, si la cantidad es positiva y no hay línea la creamos
		if (($linea == NULL) && ($cantidad > 0)){
			//Creamos una línea nueva:
			$linea = new LineaCesta(new OpcionesWidget(array('objetoPedido' => $this, 'objetoArticulo' => $objetoArticulo)));
			
		}

		if ($linea != NULL){
			//Ejecutamos el proceso de las líneas que asigna la cantidad y recalcula los precios
			$linea->setCantidad(new OpcionesWidget(array(	//Aquí no hace falta pasar el special_id
				'cantidad' => $cantidad,
				'oferta' => $oferta,
				'lote' => $lote,
				'puntos_gastados' => $puntos_gastados,
				'special_id' => $special_id
			)));
			//Grabamos la línea
			$linea->grabar();
			//Ahora tenemos en cuenta si es un borrado o no
			if ($linea->cantidad == 0){
				unset($this->lineas[$linea->codigo]);
			}else{
				//Si no se ha borrado nada, quitamos la línea de la sesión:
				Sesion::remove('lastDeleted');
				//echo("codigo insertado: " . $linea->codigo . "\n");
				$this->lineas[$linea->codigo] = $linea;
			}
		}
		
		// ******* Pedro 12/Julio/2019 *******
		//Si estamos añadiendo un lote, lo asociamos a la cesta:
		if ($lote != NULL){
			if ($cantidad > 0){
				$this->addLote($lote);
			}else{
				$this->removeLote($lote);
			}
		}
	}
	
	public static function findInCart($product, $options = NULL){
		$cart = static::getCart();
		return($cart->findProduct($product, $options));
	}
	
	public function findProduct($product, $options = NULL){
		//Pedro 18/Julio/2019 ==> Se puede mandar un único parámetro con todo en él incluido
		$options = new ObOptions($options);
		if (is_array($product)){
			$options = $product;
			$options = new ObOptions($options);
		}
		//var_dump($options);
		$product = $options->getDefaultValue('producto', $product);
		$lote = $options->getDefaultValue('lote', NULL);
		$oferta = $options->getDefaultValue('oferta', NULL);
		$puntos_gastados = $options->getDefaultValue('puntos_gastados', NULL);
		$special_id = $options->getDefaultValue('special_id', '');
		//Corrección de datos:
		if (is_object($oferta)){
			$oferta = $oferta->codigo;
		}
		if (is_object($lote)){
			$lote = $lote->codigo;
		}
		$linea = NULL;
		if (is_array($this->lineas)){
			foreach($this->lineas as $clave => $valor){
				//Vamos a buscar por ean o por código:
				if ( ($valor->ean == $product->ean) || ($valor->articulo == $product->codigo) ){
					
					//El producto está en la cesta, tenemos que ver si el lote o la oferta coinciden:
					if ( (intval($lote) == intval($valor->codigo_lote) ) && 
							(intval($oferta) == intval($valor->codigo_oferta) ) &&
							(floatval($puntos_gastados) == floatval($valor->puntos_gastados) ) &&
							(trim($special_id) == trim($valor->special_id) )
					){
						$linea = $valor;
						break;
					}
				}
			}
		}
		return($linea);
	}

	//Esta función indica si podemos finalizar el pedido o no
		//Valores:
			//200 => Todo ok
			//0 ===> Estado sin chequear
			//1 ===> Tipo de envío no seleccionado
			//2 ===> Forma de pago no seleccioanda
			//3 ===> Tipo de envío y forma de pago no seleccionada
			//4 ===> Es necesario que haya un cliente para realizar el pedido
			//5 ===> No hay productos en la cesta
			//6 ===> Faltan datos en la dirección de envío (si el envío lo requiere)
		
	public function readyToFinish(){
		$this->ready = 200;
		//Si no hay líneas, no seguimos mirando nada:
		if ($this->cantidad == 0){
			$this->ready = 5;
		}
		//El usuario tiene que ser distinto de NULL (usuario logueado o con datos de envío)
		if ($this->objeto_cliente == NULL){
			$this->ready = 4;
		}
		//Si no hay tipo de envío no seguimos:
		if ($this->objeto_tipo_envio == NULL){
			$this->ready = 1;
		}else if ($this->objeto_tipo_envio->codigo == 0){
			//FileSystem::log('Objeto tipo de envío codigo 0');
			$this->ready = 1;
			//Pedro 30/Septiembre/2020 ==> Puede ocurrir que al cambiar de ordenador se reinicie la forma de pago y por tanto no la tenemos seteada
            if (is_object($this->objeto_forma_pago)) {
                if ($this->objeto_forma_pago->codigo == 0) {
                    $this->ready = 3;
                }
            }else{
				$this->ready = 3;
			}
		}else{
			//Miramos si el tipo de envío tiene envío:
			$direccion_envio = $this->getShippingDestination();
			if (! ($this->objeto_tipo_envio->sin_envio)){
				if ( (trim($direccion_envio->nombre) == '') || (trim($direccion_envio->direccion) == '') || (trim($direccion_envio->cod_postal) == '') || (trim($direccion_envio->localidad) == '') || (trim($direccion_envio->provincia) == '') ){
					$this->ready = 6;
				}
			}

			//Hay tipo de envío ¿Se requiere pago?
			if ($this->objeto_tipo_envio->requiere_pago){
				if ($this->objeto_forma_pago->codigo == 0){
					$this->ready = 2;
				}
			}
		}
		if ($this->ready == 200){
			//EL pedido se puede finalizar, pero ojo, es posible que se pague mediante pasarela, en cuyo caso hay que indicarlo:
			if ($this->objeto_forma_pago->pasarela){
				$this->ready = 201;
			}
		}
		//FileSystem::log("El error es: " . $this->ready);
		return($this->ready);
	}
	public static function remoteReadyToFinish($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$cart = static::getCart();
		$retorno->estado = $cart->readyToFinish();
		$retorno = json_encode($retorno);
		return($retorno);
	}
	//Finalizamos el pedido grabándolo en la BD en la tabla de pedidos y enviando los emails que sean correspondientes:
		//Devuelve el código del pedido creado
	public function finish(){
		return(PedidoCliente::newOrderFromCart($this));
	}
	
	public static function remoteFinish($data){
		$retorno = new stdClass();
		ob_start();
		if (is_string($data)){
			$data = json_decode($data);
		}
		$data = new ObOptions($data);
		$crear_numero_pedido = $data->getDefaultValue('crear_numero_pedido', true);

		
		$cart = CabeceraCesta::getCart();
		if ($crear_numero_pedido){
			$cart->incrementarNumeroPedido();	//Nos aseguramos de que haya un número de pedido nuevo
		}
		$retorno->objeto_pedido = $cart->finish();
		$retorno->msg = ob_get_clean();
		//OJO, aquí nos han dado el pedido con las líneas que a su vez apuntan a la cabecera, vamos a quitar las líneas para que no haya problemas:
		unset($retorno->objeto_pedido->lineas);
		//$retorno->objeto_pedido = new PedidoCliente(6);
		$retorno = json_encode($retorno);
		return($retorno);
	}
	
	//Recalcula y devuelve los gastos de envío
	public function shippingCosts(){
		//FileSystem::log('Inicio shipping costs');
		//Aquí vamos a meter más adelante el cálculo de los gastos de envío, por ahora son 0
		$this->g_envio = 0;
		$gastos_envio_gratis = false;
		//Vamos a ver si hay un código promocional con gastos de envío gratis en cuyo caso no nos molestamos en cargar nada:
		if ($this->objeto_codigo_promocional != NULL){
			$gastos_envio_gratis = $this->objeto_codigo_promocional->g_envio_gratis;
		}
		if (!$gastos_envio_gratis){
			//Si no hay total, no hay gastos de envío:
			if ($this->cantidad > 0){
				if ($this->objeto_tipo_envio != NULL){
					$this->g_envio = $this->objeto_tipo_envio->getGastosEnvio('', $this);
				}
			}
		}
		//FileSystem::log('Fin Shipping Costs');
		return($this->g_envio);
	}
	
	public static function currentShippingCosts(){
		//Aquí vamos a meter más adelante el cálculo de los gastos de envío, por ahora son 0
		$retorno = 0;
		$cart = static::getCart();
		return($cart->shippingCosts());
	}

	public function empty(){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		foreach($this->lineas as $clave => $linea){
			$linea->setCantidad(new OpcionesWidget(array(
					'cantidad' => 0,
					'oferta' => $linea->oferta,
					'lote' => $linea->lote,
					'puntos_gastados' => $linea->puntos_gastados
				)));
			$linea->grabar();
			unset($this->lineas[$clave]);
		}
		//Cuando se vacía la cesta hay que quitar los lotes:
		unset($this->lotes);
		$this->lotes = array();
		$this->recalculate();
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	
	public static function emtpyCart(){
		Sesion::remove("lastDeleted");
		//Vamos a actualizar los componentes que nos han pasado:
		$cart = static::getCart();
		$cart->empty();
	}
	//------------------------------------------------------------------------------------------
	//Funciones remotas
	//------------------------------------------------------------------------------------------
	public static function remoteDiscardRestoreProduct($data){
		Sesion::remove("lastDeleted");
		//Vamos a actualizar los componentes que nos han pasado:
		$retorno = new stdClass();
		//Por último hacemos un commit de la sesión:
		Sesion::commit();
		$retorno = json_encode($retorno);
		return($retorno);
	}
	
	public static function remoteEmtpyCart($data){
		static::emtpyCart();
		$retorno = new stdClass();
		//TRAZAS: Vamos a ver si se ha recalculado bien el total de la cesta:
		//Por último hacemos un commit de la sesión:
		Sesion::commit();
		$retorno = json_encode($retorno);
		return($retorno);
	}
	
	public function deleteCart(){
		//Pedro 07/Septiembre/2020 No tiene sentido que quitemos la cesta de la estática, pues, si estamos en ella, ya lo hemos hecho
		//$cesta_estatica = (static::$cart == $this) ? true : false;
		//si hay líneas, vacío el carrito:
		if (count($this->lineas) > 0){
			$this->empty();
		}
		//Ahora hago el delete:
		$currentCart = static::getCart(false);
		//Pedro 07/Septiembre/2020 Vamos a coger el código de cesta para borrarla por completo porque vamos a hacer un unset que la va a limpiar:
		$codigoCesta = $this->codigo;
		//Si el carrito de la sesión es el que estamos borrando, hay que quitarlo también de la sesión
		if ($currentCart != NULL){
			if ($currentCart->codigo == $this->codigo){
				static::$cart == NULL;
			}
		}
		//Ahora eliminamos la dirección asociada:
		$sentencia_sql = 'DELETE FROM `tmp_cabecera_pedidos_direccion` WHERE `tmp_cabecera_pedidos` = \'' . $codigoCesta . '\'; ';
		//por último, borramos de la BD:
		$sentencia_sql .= 'DELETE FROM `' . static::getNombreTablaSinAlias() . '` WHERE `codigo` = \'' . $codigoCesta . '\'; ';
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$bd->setConsultaSQL($sentencia_sql);
		}
		//Pedro 07/Septiembre/2020 No tiene sentido que quitemos la cesta de la estática, pues, si estamos en ella, ya lo hemos hecho
		/*if ($cesta_estatica){
			static::$cart = $this;
		}*/
	}
	
	public static function remoteDeleteCart(){
		$cart = static::getCart();
		//si hay líneas, vacío el carrito:
		//Pedro 07/Septiembre/2020 ==> SI vamos a hacer un "deleteCart" para qué repetir todo esto, lo comento
		/*if (count($cart->lineas) > 0){
			$cart->empty();
		}
		//Ahora hago el delete:
		$currentCart = static::getCart(false);
		//Si el carrito de la sesión es el que estamos borrando, hay que quitarlo también de la sesión
		if ($currentCart != NULL){
			if ($currentCart->codigo == $cart->codigo){
				static::$cart == NULL;
			}
		}*/
		//Borramos la cesta utilizando la función propia:
		$cart->deleteCart();
	}

	//Pedro 11/Abril//2019 ==> Vamos a asegurarnos con esta función de que esta cesta tiene código, para eso sólo metemos el hash 
	private function setCode(){
		//FileSystem::log("Seteamos el código, tememos código " . $this->codigo);
		$cesta_estatica = (static::$cart == $this) ? true : false;
		if ($this->codigo == 0){
			$bd = new BaseDatos();
			$hash = Sesion::getHashSesion();
			if ($bd->isConectado()){
				$sentencia_sql = 'INSERT INTO `' . static::getNombreTablaSinAlias() . '` ( `hash` )
							VALUES (\'' . $hash . '\') 
							ON DUPLICATE KEY UPDATE `hash` = VALUES(`hash`) ;';
				$bd->setConsultaSQL($sentencia_sql);
				//FileSystem::log($sentencia_sql);
				$sentencia_sql = 'SELECT ' . static::getCampos(true) . ' 
								FROM ' . static::getNombreTabla(true) . ' 
								WHERE ' . static::getWhereTabla(NULL, NULL, $hash, false);
				$bd->setConsultaSQL($sentencia_sql);
				$fila = $bd->getFila();
				$this->codigo = $this->getProp($fila, 'codigo');
			}
		}
		//Pedro 07/Septiembre/2020 ==> Si no tenemos una cesta creada es porque esta es la cesta que queremos
		if ($cesta_estatica || static::$cart == NULL ){
			static::$cart = $this;
		}
		
		//Pedro 01/Abril/020 ==> Nos aseguramos de que en la sesión haya algo:
		if (Sesion::hasValues()){
			Sesion::set('DUMMY', 'DUMMUVALUE');
		}else{
			Sesion::remove('DUMMY');
		}
	}
	
	//Pedro 22/Julio/2019 ==> Recalculamos la cesta de manera dinámica:
	public static function recalculateCart(){
		$cart = static::getCart();
		$cart->setCode();
		$cart->recalculate();
		static::$cart = $cart;
	}
	
	//Pedro 11/Abril/2019 ==> Estas funciones grabarán en la tabla de la cabecera otros datos como Tipo de envío, forma de pago, gastos de envío, totales, etc:
	public function recalculate(){
		$this->cantidad = 0;
		$this->base = 0;
		$this->importe = 0;
		$this->importe_sin_descuento = 0;
		$this->puntos = 0;
		$this->puntos_gastados = 0;
		$this->descuento = 0;
		
		//FileSystem::log('Inicio recálculo');
		//Recalculamos las líneas:
		foreach($this->lineas as $clave => $valor){
			//FileSystem::log('Importe ' . $valor->importe);
			//Vamos a recalcular cantidades e importes:
			$cantidad_sumar = $valor->cantidad;
			$base_sumar = $valor->base;
			$importe_sumar = $valor->importe;
			if ($valor->linea_lote != NULL){
				if ($valor->linea_lote->trabaja_cantidades || $valor->linea_lote->trabaja_cantidades_pvp){
					//Pedro 26/Julio/2019 ==> Si hay cantidades/pvp sumamos 0 y el lote sumará 1
					$cantidad_sumar = 0;
					//$cantidad_sumar = $valor->cantidad * $valor->linea_lote->cantidad;
				}
			}
			$this->cantidad += $cantidad_sumar;
			if ($valor->lote != NULL){
				if ($valor->lote->precio_web > 0){
					$base_sumar = 0;
					$importe_sumar = 0;
				}
			}
			$this->base += $base_sumar;
			$this->importe += $importe_sumar;
			$this->puntos += $valor->puntos;
			$this->puntos_gastados += $valor->puntos_gastados;
		}
		
		//FileSystem::log('Chequeamos lotes');
		
		//Sumamos ahora el coste que podamos tener de los lotes:
		foreach($this->lotes as $codigo_lote => $lote){
			//FileSystem::log("Línea de lote");
			$cantidad_lote = 0;
			//Pedro 22/Julio/2019 ==> Puede que haya más de un lote en este caso vamos a calcularlo en función de la cantidad:
			if ($lote->trabaja_cantidades_pvp){
				//echo("Trabaja cantidades y buscamos el lote " . $lote->codigo . "\n");
				$lineas_lote = $this->lineas_lote($lote);
				//echo("2.-Tenemos: " . count($this->lineas) . " líneas y " . count($lineas_lote) . "\n");
				//Como trabajo cantidades y pvp, con una sóla línea se la cantidad del lote:
				foreach($lineas_lote as $clave => $linea_cesta){
					//echo("Línea $clave\n");
					//si no es optativa suma:
					if (!$linea_cesta->linea_lote->optativa){
						//echo("Optativa\n");
						$cantidad_lote += $linea_cesta->cantidad;
					}
				}
			}
			//echo("Cantidades: " . $cantidad_lote . " " . $lote->cantidad_fija . "\n");
			$cantidad_lote = ($lote->cantidad_fija == 0) ? 0 : intval($cantidad_lote / $lote->cantidad_fija);
			
			if ($lote->trabaja_cantidades || $lote->trabaja_cantidades_pvp){
				$this->cantidad += $cantidad_lote;
			}
			
			//Pedro 12/Julio/2019 ==> Por ahora el iva de los anticipos lo cobramos al 21%
			$this->base += ( $lote->base_precio_web * $cantidad_lote);
			$this->importe += ( $lote->precio_web * $cantidad_lote);
		}
		
		//FileSystem::log('Descuento global');
		//echo("Despues de recalcular tenemos: " . $this->cantidad . " elementos por un valor de : " . $this->importe . "euros\n ");
		//Ahora vamos a calcular el importe sin el descuento:
		//OJO esto está mal, Este campo es sin restar el descuento
		$this->importe_sin_descuento += Price::setDiscount($this->importe, $this->descuento);
		//Aplicamos código promocional
		//FileSystem::log('Código promocional');
		$this->applyDiscountCode();
		//FileSystem::log('shiping type');
		//Pedro 26/Mayo/2020 ==> Antes aquí se hacía un cálculo complejo con los gastos de envío. Vamos, simplemente, a ejecutar la función que asigna el tipo de envío:
		$this->setShippingType($this->tipo_envio);
		//echo("TENEMOS: " . $this->importe . "\n");
		//FileSystem::log('FIN');
	}
	
	private function setCustomer($opciones = NULL){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		//Este bloque de código es para asegurarnos de que nos están pasando un cliente o un código de cliente y tener ambos después:

		//Si hemos recibido un número, entonces nos han mandado el código de cliente
		if ( (is_numeric($opciones)) || (is_string($opciones))){
			$opciones = new ObOptions(array('codigoCliente' => $opciones));
		}else{
			$opciones = new ObOptions($opciones);
		}
		//Valores que hemos podido recibir:
		$objetoCliente = $opciones->getDefaultValue('objetoCliente', NULL);
		$codigoCliente = $opciones->getDefaultValue('codigoCliente', '');
		//Esto lo vamos hacer así para optimizar:
		if ( ($objetoCliente == NULL) && ($codigoCliente != '') ){
			$objetoCliente = new Usuario($codigoCliente);
		}
		if ($objetoCliente == NULL){
			$objetoCliente = $this->objeto_cliente;
		}
		//Si tenemos objeto cliente, el código de cliente lo cogemos del mismo:
		if ($objetoCliente != NULL){
			$codigoCliente = $objetoCliente->codigo;
		}
		//Este bloque que viene ahora es para asignar el cliente a la cesta:		
		//Pedro 15/Octubre/2019
		//Llegados a este punto, ya tenemos objeto cliente y código de cliente.
		//Ahora hay que setear el objeto cliente y el código de cliente en la clase cart:
		//Pedro 27/Enero/2019 ==> Si el cliente que vamos a asignar es el mismo, no lo asignamos:
		//Pedro 26/Febrero/2020 ==> Cuando el cliente es un "usuarioSinRegistro" no podemos comparar el código:
		if ( ($this->cliente != $codigoCliente) || (is_a($objetoCliente, 'UsuarioSinRegistro')) ){
			$this->cliente = $codigoCliente;
			$this->objetoCliente = $this->objeto_cliente = $objetoCliente;
			if ($this->objeto_cliente != NULL){
				//Si tiene código tenemos que guardarlo aquí y tener en cuenta que la dirección se coge del cliente
				if ($objetoCliente->codigo != ''){
					//Lo guardamos en este pedido:
					$this->setCustomerCode($objetoCliente->codigo);
					$obDireccion = $objetoCliente->getCurrentDirection();
				}else{
					//En caso contrario la dirección se coge de la cesta
					$opciones = array(
						'cliente' => '',
						'direccion' => $this->direccion,
						'nombre' => ($objetoCliente->nombre_comercial != '') ? $objetoCliente->nombre_comercial : $objetoCliente->nombre_legal,
						'cod_postal' => $this->cod_postal,
						'localidad' => $this->localidad,
						'provincia' => $this->provincia,
						'pais' => $this->pais,
						'telefono' => $this->telefono
					);
					$obDireccion = new Direcciones($opciones);
				}
				//Ahora vamos a a guardar el resto de datos en la tabla correspondiente:
				if ($obDireccion != NULL){
					//FileSystem::log("Asignamos la dirección");
					$this->setShippingDestination(array('objetoDireccion' => $obDireccion));
				}
				//Recalculamos:
				//FileSystem::log("Recalculamos la cesta");
				$this->recalculate();
			}
		}
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	
	private function setCustomerCode($code){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		$bd = new BaseDatos();
		$hash = Sesion::getHashSesion();
		//Pedro 15/Octubre/2019 ==> Nos Aseguramos de que haya un código en la cesta:
		$this->setCode();
		if ($bd->isConectado()){
			$sentencia_sql = 'UPDATE ' . static::getNombreTabla(true) . ' SET  `cliente` = \'' . $code . '\'
							WHERE ' . static::getWhereTabla(NULL, NULL, $hash, false);
			$bd->setConsultaSQL($sentencia_sql);
			//echo($sentencia_sql);
			//FileSystem::log("Seteamos el cliente:\n{$sentencia_sql}");
		}
		$this->cliente = $code;
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	
	//Pedro 12/Abril/2019 ==> Asignamos tipo de envío y forma de pago
	public function setShippingType($options){
		//26/Mayo/2020 ==> Cambiamos esta función de manera radical porque, entre otras cosas, recalcula la cesta, cosa que es innecesaria, al cargar el tipo de envío ya tengo el importe:
		$cesta_estatica = (static::$cart == $this) ? true : false;
		if (is_numeric($options)){
			$options = new ObOptions(array('tipo_envio' => $options));
		}
		$options = new ObOptions($options);
		$tipo_envio = $options->getDefaultValue('tipo_envio', 0);
		//Nos aseguramos de que tenga un código el tipo de envío
		$this->setCode();
		//FileSystem::log('Cargamos tipo envío');	
		//Siempre voy a tratar de asignar el tipo de envío porque esta función sirve para recalcular los gastos de envío si procede
		//EN este caso siempre voy a necesitar un tipo de envío porque:
			//Si el que me pasan es 0, yo tengo que comprobar si el tipo de envío actual es correcto
			//Si el que me pasan NO es 0, tengo que asignarlo al pedido si procede
		// $pais = NULL;
		// $codigo_postal = NULL;
		// $direccion = $this->objetoCliente->getCurrentDirection();
		// if ($direccion != NULL) {
			// $pais = $direccion->pais;
			// $codigo_postal = $direccion->cod_postal;
		// }
		//Pedro 26/mayo/2020 ==> Añadimos al tipo de envío el control de los productos de destino
		$objetoTipoEnvio = new TipoEnvioCliente( 
			new ObOptions(array(
				'codigoEnvio' => (($tipo_envio > 0) ? $tipo_envio : $this->tipo_envio), 
				'importe' => $this->importe,
				'pais' => $this->pais,
				'codigo_postal' => $this->cod_postal,
				'productos_destino' => $this->getProductType()
			))
		);
		//FileSystem::log('tipo envío cargado');
		//Si no se ha podido cargar un tipo de envío, ponsmo el código también a 0:
		$tipo_envio = $objetoTipoEnvio->codigo;
		
		//Sea cual sea el resultado, lo asigno. De esta manera, además, estoy recalculando el tipo de envío actual
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$sentencia_sql = 'UPDATE ' . static::getNombreTabla(true) . ' SET  `tipo_envio` = \'' . $tipo_envio . '\'
							WHERE ' . static::getWhereTabla(NULL, NULL, $this->hash, false);
			$bd->setConsultaSQL($sentencia_sql);
			//echo($sentencia_sql);
			//Si no puedo guardarlo en la BD no lo asigno a la cesta
			$this->tipo_envio = $tipo_envio;
			$this->objeto_tipo_envio = $objetoTipoEnvio;
			//Si el pedido no requiere pago, hay que borrar el pago:
			//Pedro 27/Julio/2020 ==> Esto estaba mal, Hay que comprobar que el tipo de envío no requiera pago y en la condición habíamos puesto
				// que no requiera envío. Por eso los "recoger" en tienda no funcionaban cuando había pago
			if (!$this->objeto_tipo_envio->requiere_pago){
				$this->setPaymentType(array('forma_pago' => 0));
			}
			//FileSystem::log('Gastos envio');
			//Nos aseguramos de asignar los gastos de envío a la cesta:
			$this->shippingCosts();
			//FileSystem::log('fin gastos envio');
			//Por último, si estamos en la cesta estática, tenemos que reasignarla:
			if ($cesta_estatica){
				static::$cart = $this;
			}
		}

	}
	public static function remoteSetShippingType($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$cart = static::getCart();
		$cart->setShippingType($data);
		$retorno = json_encode($retorno);		
		return($retorno);
	}

	public function setPaymentType($options){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		if (is_numeric($options)){
			$options = new ObOptions(array('forma_pago' => $options));
		}
		$options = new ObOptions($options);
		$forma_pago = $options->getDefaultValue('forma_pago', 0);
		//Nos aseguramos de que tenga un código el tipo de envío
		$this->setCode();
		//Pedro 26/Mayo/2020 ==> Cambiamos la manera en que se asigna la forma de pago. Lo primero es ver si el objeto es correcto:
		$objetoFormaPago = new Formaspago($forma_pago);
		$forma_pago = $objetoFormaPago->codigo;
		//Ahora actualizamos la BD y el resto de datos:
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$sentencia_sql = 'UPDATE ' . static::getNombreTabla(true) . ' SET  `forma_pago` = \'' . $forma_pago . '\'
							WHERE ' . static::getWhereTabla(NULL, NULL, $this->hash, false);
			$bd->setConsultaSQL($sentencia_sql);
			//FileSystem::log($sentencia_sql);
		}
		$this->forma_pago = $forma_pago;
		$this->objeto_forma_pago = $objetoFormaPago;
		//Aquí antes recalculábamos el pedido pero es algo que no tiene sentido. Cambiar la forma de pago no tiene que recalcular nada más	
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	public static function remoteSetPaymentType($data){
		//FileSystem::log($data);
		if (is_string($data)){
			$data = json_decode($data);
		}
		$cart = static::getCart();
		$cart->setPaymentType($data);
		$retorno = new stdClass();
		$retorno = json_encode($retorno);
		return($retorno);
	}
	
	public static function getShippingType($object = false){
		$retorno = 0;
		$cart = static::getCart();
		if ($cart != NULL){
			if ($object){
				$retorno = $cart->objeto_tipo_envio;
			}else{
				$retorno = $cart->tipo_envio;
			}
		}
		return($retorno);
	}
	
	public static function getPaymentType(){
		$retorno = 0;
		$cart = static::getCart();
		if ($cart != NULL){
			$retorno = $cart->forma_pago;
		}
		return($retorno);
	}
	//Pedro 29/Julio/2019 ==> Limpiamos las direcciones de envío
	function clearShippingDestination(){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		$sentencia_sql .= ' UPDATE `' . static::getNombreTablaSinAlias() . '` SET `direccion_envio` = \'0\' WHERE codigo = \'' . $this->codigo . '\' ;';
		$sentencia_sql .= ' DELETE FROM `tmp_cabecera_pedidos_direccion` WHERE `tmp_cabecera_pedidos` = \'' . $this->codigo . '\';';
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$bd->setConsultaSQL($sentencia_sql);
		}
		$this->nombre = (isset($this->objeto_cliente)) ? $this->objeto_cliente->nombre_comercial : '';
		$this->email = '';
		$this->direccion = '';
		$this->cod_postal = '';
		$this->localidad = '';
		$this->provincia = '';
		$this->pais = '';
		$this->zona = '';
		$this->telefono ='';
		$this->editando = false;	//Indicamos que YA NO estamos editando la dirección
		$this->creando = false;	//Indicamos que YA NO estamos creando una nueva dirección
		//recalculamos
		$this->recalculate();
		//Si la cesta estática es esta, actualizamos:
		if (static::$cart != NULL){
			if (static::$cart->codigo != $this->codigo){
				static::$cart = $this;
			}
		}
	}
	function setShippingDestination($options){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		//Si recibimos un código, es la direección de envío:
		if (is_numeric($options)){
			$options = array('codigoDireccion' => $options);
		}

		//El objeto usuario lo cogemos de la cesta. No de "logueado" esto lo vamos a ir quitando porque la cesta ya cogió al usuario en su momento:
		//var_dump($this->objeto_cliente);
		if ($this->objeto_cliente != NULL){
			$options = new ObOptions($options);
			$email = $options->getDefaultValue('email', $this->objeto_cliente->email);
			$codigoDireccion = $options->getDefaultValue('codigo', 0);
			$objetoDireccion = $options->getDefaultValue('objetoDireccion', NULL);
			$nombre = $options->getDefaultValue('nombre', '');
			$direccion = $options->getDefaultValue('direccion', '');
			$telefono = $options->getDefaultValue('telefono', '');
			$cod_postal = $options->getDefaultValue('cod_postal', '');
			$localidad = $options->getDefaultValue('localidad', '');
			$provincia = $options->getDefaultValue('provincia', '');
			$pais = $options->getDefaultValue('pais', '');
			$zona = $options->getDefaultValue('zona', 0);
			$bd = new BaseDatos();
			//Si no tenemos objeto dirección hay que crearlo:
			if ($objetoDireccion == NULL){
				//FileSystem::log("Creamos objeto dirección");
				$objetoDireccion = new Direcciones(array('codigo' => $codigoDireccion, 'direccion' => $direccion, 'cod_postal' => $cod_postal, 'localidad' => $localidad,
										'provincia' => $provincia, 'telefono' => $telefono, 'nombre' => $nombre, 'pais' => $pais, 'zona' => $zona));
			}
			//var_dump($objetoDireccion);
			//Llegados a este punto, si tenemos un objeto dirección, lo guardamos:
			if ($objetoDireccion != NULL){
				//echo("Nos han pasado dirección\n");
				//FileSystem::log("Seteamos un código");
				$this->setCode();	//Nos aseguramos de tener un código para este pedido
				$sentencia_sql = '';
				//OJO, si tenemos un usuario hay que actualizar el objeto dirección
				if ($this->objeto_cliente->codigo != ''){
					//En este caso tenemos que actualizar la dirección de envío:
					//FileSystem::log('Guardamos dirección desde la cesta');
					//FileSystem::log("Grabamos el objeto dirección");
					$objetoDireccion->save();
					//Además vamos a ponerle al usuario logueado la dirección que acabamos de guardar:
					//FileSystem::log("Actualizamos la dirección del clienet");
					Usuario::logueadoUpdateDireccion($objetoDireccion);
					//SI el objeto direccion tiene un código diferente al que hemos seleccionado tenemos que actualizar la dirección de envío por defecto del cliente:
						//Pedro 24/Abril/2019 ==> Antes teníamos en cuenta también que la dirección nueva tuviese un código, pero se da el caso al cerrar la sesión que queremos quitar la dirección de envío asociada
					if ( $objetoDireccion->codigo != $this->codigoDireccion){
						//Guardamos en el cliente el código de la dirección por defecto
						//TODO
						$this->codigoDireccion = $objetoDireccion->codigo;
						$sentencia_sql .= ' UPDATE `' . static::getNombreTablaSinAlias() . '` SET `direccion_envio` = \'' . $this->codigoDireccion . '\' WHERE codigo = \'' . $this->codigo . '\' ;';
						//Pedro 31/Agosto/2020 ==> Actualizamos también la direcciíon de envío por defecto en el cliente:
						$sentencia_sql .= ' UPDATE `' . Usuario::getNombreTablaSinAlias() . '` SET `direc_predeterminada` = \'' . $this->codigoDireccion . '\' WHERE codigo = \'' . $this->objeto_cliente->codigo . '\' ;';
						//FileSystem::log("Reasignamos el índice de dirección");
						$this->objeto_cliente->assignAdressIndex($this->codigoDireccion);
						//echo($sentencia_sql);
					}
					
				}
				//actualizamos la dirección sólo si es distinta:
				$currentShippingDestination = $this->getCurrentShippingDestination();
				
				//echo("dirección actual: ");
				//var_dump($currentShippingDestination);
				//echo("dirección nueva: ");
				//var_dump($objetoDireccion);
				if ( $currentShippingDestination != $objetoDireccion){
					//echo("Es distinta de la actual\n");
					//Eliminamos cualquier dirección que haya para esta cesta y creamos una nueva:
					$sentencia_sql .= 'DELETE FROM `tmp_cabecera_pedidos_direccion` WHERE `tmp_cabecera_pedidos` = \'' . $this->codigo . '\';';
					//Si todos los campso están vacíos no creamos apunte ninguno:
					/*if ($this->creando){
						$a = $currentShippingDestination;	//Voy a usar una variable de nombre corto para simplificar estas líneas de código
					}else{*/
						$a = $objetoDireccion;
					//}
					if (trim($a->nombre . $email . $a->direccion . $a->cod_postal . $a->localidad . $a->provincia . $a->pais . $a->zona . $a->telefono) != ''){
						//Creamos un nuevo apunte en las direcciones de la cesta
						$sentencia_sql .= ' INSERT INTO `tmp_cabecera_pedidos_direccion` 
							(`tmp_cabecera_pedidos`, `nombre`, `email`, `direccion`, `cod_postal`, `localidad`, `provincia`, `pais`, `zona`, `telefono`)
							VALUES
							(' . $bd->real_escape_string($this->codigo) . ', ' . $bd->real_escape_string($a->nombre) . ', ' . $bd->real_escape_string($email) . ', 
							' . $bd->real_escape_string($a->direccion) . ', ' . $bd->real_escape_string($a->cod_postal) . ', 
							' . $bd->real_escape_string($a->localidad) . ', ' . $bd->real_escape_string($a->provincia) . ', ' . $bd->real_escape_string($a->pais) . ', 
							' . $bd->real_escape_string($a->zona) . ', ' . $bd->real_escape_string($a->telefono) . ' );';
					}
				}

				//Ejecutamos el sql
				if ($sentencia_sql != ''){
					if ($bd->isConectado()){
						//echo("Lanzamos la actualización de las tablas\n$sentencia_sql\n");
						$bd->setConsultaSQL($sentencia_sql);
					}
				}
				$this->nombre = $objetoDireccion->nombre;
				$this->email = $email;
				$this->direccion = $objetoDireccion->direccion;
				$this->cod_postal = $objetoDireccion->cod_postal;
				$this->localidad = $objetoDireccion->localidad;
				$this->provincia = $objetoDireccion->provincia;
				$this->pais = $objetoDireccion->pais;
				$this->zona = $objetoDireccion->zona;
				$this->telefono = $objetoDireccion->telefono;
				$this->editando = false;	//Indicamos que YA NO estamos editando la dirección
				$this->creando = false;	//Indicamos que YA NO estamos creando una nueva dirección
				//recalculamos
				//FileSystem::log("Recalculamos la cesta desde la asignación de la dirección");
				$this->recalculate();	//Comentado en pruebas
				//Si la cesta estática es esta, actualizamos:
				if ( (static::$cart != NULL) && (static::$cart->codigo != $this->codigo) ){
					static::$cart = $this;
				}
			}else{
				echo('Error, no hay objeto direccion');
			}
		}else{
			echo('Error, no hay objeto cliente');
		}
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	public static function remoteSetShippingDestination($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		ob_start();
		//Cogemos la cesta
		$cart = static::getCart();
		$retorno = new stdClass();
		
		$data = new ObOptions($data);	//Nos aseguramos de que "data" tenga el formato que necesitamos
		$codigoDireccion = $data->getDefaultValue('codigo', 0);
		$nombre = $data->getDefaultValue('nombre', NULL);
		$direccion = $data->getDefaultValue('direccion', NULL);
		$telefono = $data->getDefaultValue('telefono', NULL);
		$cod_postal = $data->getDefaultValue('cod_postal', NULL);
		$localidad = $data->getDefaultValue('localidad', NULL);
		$provincia = $data->getDefaultValue('provincia', NULL);
		$pais = $data->getDefaultValue('pais', NULL);
		$zona = $data->getDefaultValue('zona', NULL);
		//Pedro 24/Abril/2019 ==> El array que crea las direcciones de envío no va a llevar aquellos datos que no nos pasen para no guardar una diercción vacía:
		$array_datos_direccion = array('direccion' => $direccion, 'cod_postal' => $cod_postal, 'localidad' => $localidad, 'provincia' => $provincia, 
										'telefono' => $telefono, 'nombre' => $nombre, 'pais' => $pais, 'zona' => $zona, 'codigo' => $codigoDireccion, 'cliente' => $cart->cliente);
		//var_dump($array_datos_direccion);
		foreach($array_datos_direccion as $clave => $valor){
			if ($valor == NULL){
				unset($array_datos_direccion[$clave]);
			}
		}

		$objetoDireccion = new Direcciones($array_datos_direccion);
		//Directamente ejecutamos la función dináimca del carrito	
		$cart->setShippingDestination(array('objetoDireccion' => $objetoDireccion));
		$retorno->response = ob_get_clean();

		$retorno = json_encode($retorno);
		return($retorno);
	}
	public function getCurrentShippingDestination(){
		/*$retorno = new Direcciones(array('direccion' => $this->direccion, 'cod_postal' => $this->cod_postal, 'localidad' => $this->localidad,
												'provincia' => $this->provincia, 'telefono' => $this->telefono, 'nombre' => $this->nombre, 'pais' =>  $this->pais, 'zona' => $this->zona,
												'codigo' => $this->codigoDireccion));*/
		//Pedro 24/Abril/2019 ==> hasta ahora se creaba la dirección pasándole el código, pero no quiero crearla pasándole dicho código. lo que quiero en esta función es coger la dirección asociada a la cesta
				//Y no la dirección que hay en "direcciones"
		$retorno = new Direcciones(array('direccion' => $this->direccion, 'cod_postal' => $this->cod_postal, 'localidad' => $this->localidad,
												'provincia' => $this->provincia, 'telefono' => $this->telefono, 'nombre' => $this->nombre, 'pais' =>  $this->pais, 'zona' => $this->zona));
		//var_dump($retorno);
		return($retorno);
	}

	public static function getShippingDestination(){
		$cart = static::getCart();
		return($cart->getCurrentShippingDestination());
	}
	
	public static function remoteGetShippingDestination(){
		return(static::getShippingDestination());
	}
	
	public function setEditando($valor = true){
		$this->editando = $valor;
	}
	
	public static function remoteSetEditando($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$cart = static::getCart();
		$data = new ObOptions($data);
		$cart->setEditando($data->getDefaultValue('editando', true));
		$retorno = json_encode($retorno);		
		return($retorno);
	}
	public function getEditando(){
		return($this->editando);
	}
	
	public function setCreando($valor = true){
		$this->creando = $valor;
	}
	
	public static function remoteSetCreando($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$cart = static::getCart();
		$data = new ObOptions($data);
		$cart->setCreando($data->getDefaultValue('creando', true));
		$retorno = json_encode($retorno);		
		return($retorno);
	}
	public function getCreando(){
		return($this->creando);
	}
	
	//Pedro 23/Abril/2019 ==> Cuando nos deslogueamos de la cesta hay que eliminar la dirección de envío de la misma y después desloguearse del usuario
	public static function cartLogout(){
		//Si me creo un objeto dirección vacío y lo paso al pedido hago que se elimine el viejo y no se apunte este
		$opciones = array(
			'codigo' => '',
			'email' => '',
			'nombre' => '',
			'direccion' => '',
			'cod_postal' => '',
			'localidad' => '',
			'provincia' => '',
			'pais' => '',
			'zona' => '',
			'telefono' => ''	
		);
		$obDireccion = new Direcciones($opciones);
		$cart = static::getCart();
		//Asignamos la direccion vacía al carrito
		$cart->setShippingDestination($opciones);
		//Le borramos el cliente
		$cart->setCustomerCode('');
		//Ahora deslogueamos al cliente
		Usuario::cartLogout();
	}
	
	//Pedro 02/Mayo/2019 ==> Funciones para gestionar los códigos de descuento
	
	
	public function applyDiscountCode($code = NULL){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		$code = ($code == NULL) ? $this->codigo_promocional : $code;
		//SI no nos han pasado código promocional y no tenemos ninguno, no hacemos nada:
		//echo("Recibido código $code\n");
		if ( ($code != NULL) || ($this->codigo_promocional != '') ){
			//Para evitar recursividad, esta función va a suponer SIEMPRE que la cesta está recalculada
				//Es posible que en un momento determinado el poder aplicar un código de descuento dependa de un importe mínimo, por ejemplo
			//Antes de aplicar un código de descuento, vamos a ver si es posible:
			$codigo_promocional = new CodigosPromocionales(array('codigo_promocional' => $code));
			if ($codigo_promocional->checkDiscountCode()){
				//Aquí vamos a tener en cuenta ahora los parámetros que dependan de la cesta:
				if ($codigo_promocional->escaparates != ''){
					//En este caso el importe de descuento hay que calcularlo por línea y aplicarle a cada línea su descuento. Después de eso hay que recalcular el total
				}else{
					//En este caso el descuento es global:
					$this->descuento = $codigo_promocional->descuento;
					$this->importe = ($this->importe_sin_descuento * (100 - $this->descuento) ) / 100;
					if ($codigo_promocional->g_envio_gratis){
						$this->g_envio = 0;
					}else{
						//PEdro 09/Mayo/2019 ==> En este caso vamos a recalcular siempre los gastos de envío para curarnos en salud:
						$this->shippingCosts();
					}
				}
				//Si nos han pasado un codigo promocional distinto al que tenemos, vamos a guardarlo:
				if ($this->codigo_promocional != $code){
					//Lo actualizamos en la BD
					static::updateField(array($this->cliente, $this->codigo, $this->hash), 'codigo_promocional', $code);
					//Si el carro de la sesión es este modificamos su código promocional
					if (static::$cart == $this){
						static::$cart->codigo_promocional = $code;
					}
					//Por último a este le quitamos el código
					$this->codigo_promocional = $code;
					$this->objeto_codigo_promocional = new CodigosPromocionales(array('codigo_promocional' => $this->codigo_promocional));
				}
			}else{
				//Si el código no se puede utilizar y resulta que es el que tiene la cesta, hay que quitárselo:
				if ($this->codigo_promocional == $code){
					//Lo actualizamos en la BD
					static::updateField(array($this->cliente, $this->codigo, $this->hash), 'codigo_promocional', '');
					//Si el carro de la sesión es este modificamos su código promocional
					if (static::$cart == $this){
						static::$cart->codigo_promocional = '';
					}
					//Por último a este le quitamos el código
					$this->codigo_promocional = '';
					$this->objeto_codigo_promocional = NULL;
				}
			}
		}
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	
	public static function remoteApplyDiscountCode($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$data = new ObOptions($data);	//Nos aseguramos de que "data" tenga el formato que necesitamos
		$code = $data->getDefaultValue('code', NULL);
		$cart = static::getCart();
		$retorno->msg = $cart->applyDiscountCode($code);
		$retorno = json_encode($retorno);		
		return($retorno);
	}
	
	public function removeDiscountCode(){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		if ($this->codigo_promocional != ''){
			//Lo actualizamos en la BD
			static::updateField(array($this->cliente, $this->codigo, $this->hash), 'codigo_promocional', '');
			//Si el carro de la sesión es este modificamos su código promocional
			if (static::$cart == $this){
				static::$cart->codigo_promocional = '';
			}
			//Por último a este le quitamos el código
			$this->codigo_promocional = '';
			$this->objeto_codigo_promocional = NULL;
			$this->recalculate();
		}
		if ($cesta_estatica){
			static::$cart = $this;
		}
	}
	
	public static function remoteRemoveDiscountCode($data){
		if (is_string($data)){
			$data = json_decode($data);
		}
		$retorno = new stdClass();
		$cart = static::getCart();
		$cart->removeDiscountCode();
		$retorno = json_encode($retorno);		
		return($retorno);
	}
	
	//Pedro 25/Mayo/2019 ==> Extraemos:
		// L ==> Sólo hay Libros
		// P ==> Sölo hay papelería
		// T ==> Hay libros y papelería
		// N => No hay nada
	public function getProductType(){
		$lineas = array_values($this->lineas);
		$tipo_actual = 'N';
		$contador = 0;
		while ( ($tipo_actual != 'T') && ($contador < count($lineas)) ){
			$tipo_articulo = $lineas[$contador]->objeto_articulo->tipo;
			//En la primera vuelta, el tipo será N, después, si el tipo no es igual al que estamos mirando, es una T
			if ($tipo_actual == 'N'){
				$tipo_actual = $tipo_articulo;
			}else if ($tipo_actual != $tipo_articulo){
				$tipo_actual = 'T';
			}
			$contador++;
		}
		return($tipo_actual);
	}
	public static function staticGetProductType(){
		$cart = CabeceraCesta::getCart();
		return($cart->getProductType());
	}
	
	//Pedro 03/Septiembre/2020 ==> Si le pasamos el número de pedido es el que asignamos (util cuando fusionamos cestas)
	//Pedro 03/Junio/2019 ==> Regeneramos el número de pedido para incrementarlo:
	public function incrementarNumeroPedido($nuevoNumero = ''){
		$cesta_estatica = (static::$cart == $this) ? true : false;
		if ($cesta_estatica){
			static::$cart = $this;
		}
		//Necesitamos conexión a la BD
        if ($nuevoNumero == '') {
            //Insertamos un nuevo número:
			$nuevo_contador = BaseDatos::incrementa_contador('numero_pedido');
			if (static::$cart == $this){
				static::$cart->numero_pedido = $nuevo_contador;
			}
			$nuevo_contador = $this->numeros_pedidos_reservados . '|' . $nuevo_contador . '|';
        }else{
			$nuevo_contador = $nuevoNumero;
		}
		//Actualizamos el número de pedido en la BD
		//FileSystem::log("Cesta: {$this->codigo} con números: {$this->numeros_pedidos_reservados}");
		static::updateField(array($this->cliente, $this->codigo, $this->hash),
							 'numero_pedido', 
							 $nuevo_contador 
							);
		if ($cesta_estatica){
			static::$cart = $this;
		}
		
	}
	public static function remoteIncrementarNumeroPedido(){
		$cart = CabeceraCesta::getCart();
		$cart->incrementarNumeroPedido();
	}
	
	//Pedro 14/Octubre/2019 ==> Esta función la vamos a hacer desaparecer en el futuro
	public function pasarela($tipo_seleccionado = 1){
		$pasarela = false;
		//El campo pasarela tendrá un valor numérico asignado a cada pasarela
		if ( ($this->objeto_forma_pago != NULL) && ($this->objeto_forma_pago->pasarela == $tipo_seleccionado) ){
			$pasarela = true;
		}
		return($pasarela);
	}
	
	//Tratamiento de lotes:
	public function addLote($lote){
		$return = false;
		if (is_numeric($lote)){
			$lote = new Lote($lote);
		}
		if (is_a($lote, 'Lote')){
			$cod_lote = $lote->codigo;
			if (!isset($this->lotes[$cod_lote])){
				$this->lotes[$cod_lote] = $lote;
				$return = true;
			}
		}
		returN($return);
	}
	
	public function getLote($lote){
		$return = NULL;
		if (is_a($lote, 'Lote')){
			$lote = $lote->codigo;
		}
		if (is_numeric($lote)){
			if (!isset($this->lotes[$lote])){
				$return = $this->lotes[$lote];
			}
		}
		return($return);
	}
	
	public function removeLote($lote){
		$return = false;
		if (is_a($lote, 'Lote')){
			$lote = $lote->codigo;
		}
		if (is_numeric($lote)){
			if (isset($this->lotes[$lote])){
				unset($this->lotes[$lote]);
				$return = true;
			}
		}
		return($return);
	}
	//Creado por Pedro el 22/Julio/2019 ==> devuelve las líneas de pedido pertenecientes a un lote
	private function lineas_lote($lote){
		$return = array();
		$lote = (is_object($lote)) ? $lote->codigo : $lote;	//Nos pueden haber mandado un objeto lote o su código
		//echo("En la función tenemos " . count($this->lineas) . " líneas y buscamos el lote: $lote \n");
		foreach($this->lineas as $clave => $linea){
			//echo("Código lote " . $linea->codigo_lote . "\n");
			if (intval($linea->codigo_lote) == intval($lote)){
				$return[] = $linea;
			}
		}
		return($return);
	}
	
	//*********************************************************************************************************
	//FILTRADOS, NO AÑADIR NADA AQUÍ DENTRO QUE NO SEA UN FILTRADO
	//*********************************************************************************************************
	public static function sin_lotes($valor){
		$return = false;
		if (is_a($valor, 'LineaCesta')){
			if ($valor->codigo_lote == 0){
				$return = true;
			}
		}
		return($return);
	}
	public static function con_lotes($valor){
		$return = false;
		if (is_a($valor, 'LineaCesta')){
			if ($valor->codigo_lote > 0){
				$return = true;
			}
		}
		return($return);
	}
	//*********************************************************************************************************
	//FILTRADOS, NO AÑADIR NADA AQUÍ DENTRO QUE NO SEA UN FILTRADO
	//*********************************************************************************************************
}

?>