<?php

class Producto_model extends CI_Model {

	/* Funciones de Consulta */

	/**
	 * Entrega datos de un producto por id
	 */
	public function get($id) {
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$this->db->where('deleted', 0);
		$producto = $this->db->get()->row_array();

		$producto['ps_nombre'] = '';
		if (!empty($producto['ps_id'])) {
			$productoPS = $this->get_ps($producto['ps_id']);
			$producto['ps_nombre'] = $productoPS['nombre'];
		}

		if (!empty($producto)){
			$producto['almacenes'] = $this->stock_almacenes($id, true);
		}

		if (!empty($producto)){
			$producto['marketplaces'] = $this->marketplaces($id);
		}

		return $producto;
	}


	public function get_basico($id) {
		// Busca precios en Ventas
		$this->db->select('id, nombre, precio, ps_id, disponibles');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->where('id', $id);
		$p = $this->db->get()->row_array();

		$r = [];

		if(!empty($p)) {
			$ps_prices = [];
			if (!empty($p['ps_id'])) {
				$ps_prices = $this->ps_prices($p['ps_id'], 1);
			}

			$CI =& get_instance();
			$CI->load->model('almacen_model', 'Almacen');
			$stocks = $this->Almacen->stock_productos();

			foreach($stocks[$p['id']] as $idAlmacen => $stockAlmacen){
				$disponiblesAlmacen[$idAlmacen] = $stockAlmacen['disponibles'];
			}

			$psImage = $this->get_ps_image($p['ps_id']);

			$r = [
				'id' => $p['id'],
				'ps_id' => $p['ps_id'],
				'nombre' => $p['nombre'],
				'precio_normal' => $ps_prices['precio_normal'] ?? $p['precio'],
				'descuento' => $ps_prices['descuento'] ?? 0,
				'precio' => $ps_prices['precio'] ?? $p['precio'],
				'descuento_tienda' => $ps_prices['descuento_tienda'] ?? 0,
				'precio_tienda' => $ps_prices['precio_tienda'] ?? 0,
				'disponibles' => $p['disponibles'],
				'disponibles_almacen' => $disponiblesAlmacen,
				'id_image' => $psImage
			];
		}
		return $r;
	}


	/**
	 * Entrega resultados de productos para busquedas
	 */
	public function search($b = '') {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';
	
		$this->db->select('p.id, p.ps_id, sa.id_product, p.nombre as nombre, p.stock, sa.physical_quantity as ps_stock, p.disponibles, sa.quantity as ps_disponibles, p.id_ranking, pl.available_now, pl.available_later');
		$this->db->from('productos p');
		$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps.'.ps_product_lang pl','ON p.ps_id = pl.id_product', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->order_by('p.nombre ASC');
	
		$this->db->group_start();
			$this->db->where('p.codigo_barra', $b);
			$this->db->or_where('p.sku', $b);
			$this->db->or_group_start();
				if (!empty($b)) {
					$keywords = explode(" ", $b); 
					foreach ($keywords as $key) { 
						$this->db->like('p.nombre', $key);
					}
				}
			$this->db->group_end();
		$this->db->group_end();
	
		$productos = $this->db->get()->result_array();
		$productos = $this->agregarMarketplaces($productos);
	
		return $productos;
	}

	/**
	 * Entrega listado de productos para autocomplete
	 */
	public function autocomplete_nombre($b = '') {

		$this->db->select('p.id, p.nombre as label, p.nombre as value, p.ps_id');
		$this->db->from('productos p');
		$this->db->where('deleted', 0);
		//$this->db->order_by('nombre ASC');

		if (!empty($b)) {
			$keywords = explode(" ", $b);
			foreach ($keywords as $key => $v) {
				if ($key == 0) {
					$this->db->like('p.nombre', $v, 'after');
				}
				else {
					$this->db->like('p.nombre', $v);
				}
			}
		}

		$productos = $this->db->get()->result_array();

		$this->db->select('p.id, p.nombre as label, nombre as value, p.ps_id');
		$this->db->from('productos p');
		$this->db->where('deleted', 0);
		//$this->db->order_by('nombre ASC');

		if (!empty($b)) {
			$keywords = explode(" ", $b);
			foreach ($keywords as $key => $v) {
				$this->db->like('p.nombre', $v);
			}
		}

		$productos2 = $this->db->get()->result_array();

		$tmp = [];
		foreach($productos as $p) {
			$tmp[$p['id']] = $p;
		}

		foreach($productos2 as $p) {
			$tmp[$p['id']] = $p;
		}

		$r = [];
		foreach($tmp as $p) {
			$r[] = $p;
		}

		return $r;
	}

	/**
	 * Entrega listado de productos PS para autocomplete
	 */
	public function autocomplete_nombre_ps($b = '') {

		/*aqui*/
		$ps = $this->load->database('prestashop', TRUE);

		/*
		SELECT * FROM `ps_order_detail` WHERE product_id = XXXX
		*/

		$ps->select("p.id_product as ps_id, l.name as label, l.name as value");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);

		if (!empty($b)) {
			$keywords = explode(" ", $b);
			foreach ($keywords as $key => $v) {
				if ($key == 0) {
					$ps->like('l.name', $v, 'after');
				}
				else {
					$ps->like('l.name', $v);
				}
			}
		}

		$productos = $ps->get()->result_array();

		$ps->select("p.id_product as ps_id, l.name as label, l.name as value");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		//$this->db->order_by('nombre ASC');

		if (!empty($b)) {
			$keywords = explode(" ", $b);
			foreach ($keywords as $key => $v) {
				$ps->like('l.name', $v);
			}
		}

		$productos2 = $ps->get()->result_array();

		$tmp = [];
		foreach($productos as $p) {
			$tmp[$p['ps_id']] = $p;
		}

		foreach($productos2 as $p) {
			$tmp[$p['ps_id']] = $p;
		}

		$r = [];
		foreach($tmp as $p) {
			$r[] = $p;
		}

		return $r;
	}


	public function listado($filtros = [], $orden = []) {
		
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('
			p.*,
			sa.id_product,
			sa.physical_quantity as ps_stock,
			sa.quantity as ps_disponibles,
			pl.available_now,
			pl.available_later,
			pi.id_image as ps_imagen,
			a.id as id_almacen,
			a.nombre as nombre_almacen,
			a.stock_online,
			ap.stock as stock_almacen,
			ap.disponibles as disponibles_almacen,
			ap.reservados as reservados_almacen
		');
		$this->db->from('productos p');
		$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps.'.ps_product_lang pl','ON p.ps_id = pl.id_product', 'left');
		$this->db->join($dbps.'.ps_image pi', 'p.ps_id = pi.id_product AND pi.cover = 1', 'left');
		$this->db->join('almacenes_productos ap', 'p.id = ap.id_producto AND ap.deleted = 0', 'left');
		$this->db->join('almacenes a', 'a.id = ap.id_almacen AND a.deleted = 0', 'left');
		$this->db->where('p.deleted', 0);

		if (!empty($filtros['categoria'])) {
			$this->db->where('id_categoria', $filtros['categoria']);
		}
		if (!empty($filtros['proveedor'])) {
			$this->db->where('id_proveedor', $filtros['proveedor']);
		}
		if (!empty($filtros['estado'])) {
			// Disponible
			if ($filtros['estado'] == 1) {
				$this->db->where('p.disponibles > 0');
			}
			// Preventa
			if ($filtros['estado'] == 2) {
				$this->db->where('p.disponibles < 1');
				$this->db->where('pl.available_now', 'Preventa');
			}
			// Agotado
			if ($filtros['estado'] == 3) {
				$this->db->where('p.disponibles < 1');
				$this->db->where('pl.available_now !=', 'Preventa');
			}
		}

		if (empty($orden)) {
			$this->db->order_by('p.id', 'DESC');
		}

		$productos = $this->db->get()->result_array();

		$result = [];
		foreach ($productos as $row) {
			$id = $row['id'];

			if (!isset($result[$id])) {
				// Guardamos todas las columnas tal cual vienen
				$result[$id] = $row;
				// Creamos el subarray de almacenes
				$result[$id]['almacenes'] = [];
			}

			// Si tiene almacén asociado, lo agregamos al subarray
			if (!empty($row['id_almacen'])) {
				$result[$id]['almacenes'][$row['id_almacen']] = [
					'id'         => $row['id_almacen'],
					'nombre'     => $row['nombre_almacen'],
					'stock'      => $row['stock_almacen'],
					'disponibles'=> $row['disponibles_almacen'],
					'reservados' => $row['reservados_almacen'],
					'stock_online' => $row['stock_online']
				];
			}
		}
		$productos = $result;

		$productos = $this->agregarMarketplaces($productos);

		return $productos;
	}

	public function get_all($proveedor = false) {
		$this->db->select('p.*, pv.nombre as pv_nombre');
		$this->db->from('productos p');
		$this->db->join('proveedores pv', 'p.id_proveedor = pv.id', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->order_by('id_proveedor', 'ASC');
		$this->db->order_by('p.nombre', 'ASC');
		$productos = $this->db->get()->result_array();

		if ($proveedor) {
			foreach ($productos as $p) {
				$tmp_prductos[$p['id_proveedor']][] = $p;
			}
			$productos = $tmp_prductos;
		}

		return $productos;
	}

	/**
	 * Entrega listado de stock de todos los productos del sistema ordenados por categoría
	 */
	public function stock($b = '') {
		$this->db->select('p.*, pv.nombre as pv_nombre');
		$this->db->from('productos p');
		$this->db->join('proveedores pv', 'p.id_proveedor = pv.id', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->order_by('id_categoria', 'ASC');
		$this->db->order_by('p.nombre', 'ASC');

		if (!empty($b)) {
			$this->db->like('p.nombre', $b);
		}

		$productos = $this->db->get()->result_array();
		$tmp_prductos = [];
		foreach ($productos as $p) {
			if ($p['stock'] > 0) {
				$tmp_prductos[$p['id_categoria']]['disponibles'][] = $p;
			}
			else {
				$tmp_prductos[$p['id_categoria']]['agotados'][] = $p;
			}
		}
		$productos = $tmp_prductos;

		return $productos;
	}

	public function full_info($id) {
		if (!empty($id)) {

			//Busca info del producto
			$this->db->select('p.*, pv.nombre as pv_nombre');
			$this->db->from('productos p');
			$this->db->join('proveedores pv', 'p.id_proveedor = pv.id', 'left');
			$this->db->where('p.id', $id);
			$this->db->where('p.deleted', 0);
			$this->db->order_by('id_proveedor', 'ASC');
			$this->db->order_by('p.nombre', 'ASC');
			$producto = $this->db->get()->row_array();

			$producto['id_image'] = $this->get_ps_image($producto['ps_id']);
			$producto['ps_price'] = $this->ps_prices($producto['ps_id'], 1);

			$producto['ps_stock'] = $this->ps_stock($producto['ps_id']);
			$producto['ps_state'] = $this->ps_state($producto['ps_id']);

			//Busca compras asociadas
			$this->db->select('cp.*, c.id_proveedor, c.fecha_recepcion, c.id_tipo_compra, c.descuenta_iva, c.folio');
			$this->db->from('compras_productos cp');
			$this->db->join('compras c', 'cp.id_compra = c.id', 'left');
			$this->db->where('cp.id_producto', $id);
			$this->db->where('cp.deleted', 0);
			$this->db->where('c.deleted', 0);
			$this->db->order_by('c.fecha', 'DESC');
			$compras = $this->db->get()->result_array();
			$dc = $this->purchases_data($compras);

			// Consulta única: todas las ventas del producto
			$this->db->select('pv.*, v.*, v.fecha as v_fecha, c.nombre AS cliente_nombre, c.email AS cliente_email')
					->from('productos_ventas pv')
					->join('ventas v', 'pv.id_venta = v.id', 'left')
					->join('clientes c', 'v.id_cliente = c.id', 'left')
					->where('pv.id_producto', $id)
					->where('pv.deleted', 0)
					->where('v.deleted', 0)
					->order_by('v.fecha_venta', 'DESC');
			$ventas_all = $this->db->get()->result_array();

			// Mapear productos solo una vez
			$ventas_ids = array_column($ventas_all, 'id');
			$productos_map = [];
			if (!empty($ventas_ids)) {
				$this->db->select('pv.id_venta, p.id, p.nombre, pv.cantidad')
						->from('productos_ventas pv')
						->join('productos p', 'pv.id_producto = p.id', 'left')
						->where_in('pv.id_venta', $ventas_ids)
						->where('pv.deleted', 0);;
				$productos = $this->db->get()->result_array();
				foreach ($productos as $p) {
					$productos_map[$p['id_venta']][] = [
						'id'       => $p['id'],
						'nombre'   => $p['nombre'],
						'cantidad' => $p['cantidad']
					];
				}
			}

			// Agregar productos a cada venta
			foreach ($ventas_all as &$v) {
				$v['productos'] = $productos_map[$v['id']] ?? [];
			}

			// Inicializar variables
			$ventas = $ventas_proceso = $otras_ventas = $devoluciones = $mermas = [];
			$unidadesReservadas = $unidadesEnviadas = $unidades_proceso = $otros = 0;
			$minTime = 0;
			$ventasMeses = ['ventas'=>[]];

			// Clasificar ventas y calcular variables
			foreach ($ventas_all as &$v) {
				// Estado PS
				if ($v['ps_id'] != 0) $v['ps_state'] = $this->Venta->get_order_state($v['ps_id']);

				// Tiempo mínimo
				$time = strtotime($v['fecha_venta']);
				if (empty($minTime) || $time < $minTime) $minTime = $time;

				// Ventas por mes
				$mes = date('Ym', $time);
				$ventasMeses['ventas'][$v['id_canal']][$mes] = ($ventasMeses['ventas'][$v['id_canal']][$mes] ?? 0) + $v['cantidad'];
				$ventasMeses['ventas'][0][$mes] = ($ventasMeses['ventas'][0][$mes] ?? 0) + $v['cantidad'];

				// Sumar unidades
				if ($v['reservado'] && $v['vendido']) $unidadesReservadas += $v['cantidad'];
				if ($v['enviado']) $unidadesEnviadas += $v['cantidad'];
				if (!$v['vendido'] && $v['reservado'] && $v['id_tipo_venta']==1) $unidades_proceso += $v['cantidad'];
				if (!$v['vendido'] && !$v['reservado'] && $v['id_tipo_venta']==1) $otros += $v['cantidad'];

				// Clasificar según condición
				if ($v['vendido'] && in_array($v['id_tipo_venta'], [1,6])) $ventas[] = $v;
				elseif (!$v['vendido'] && $v['reservado'] && $v['id_tipo_venta']==1) $ventas_proceso[] = $v;
				elseif (!$v['vendido'] && !$v['reservado'] && $v['id_tipo_venta']==1) $otras_ventas[] = $v;
				elseif ($v['vendido'] && $v['id_tipo_venta']==3) $devoluciones[] = $v;
				elseif ($v['vendido'] && $v['id_tipo_venta']==4) $mermas[] = $v;
			}

			// Calcular datos finales
			$dv = $this->sales_data($ventas);
			$dd = $this->sales_data($devoluciones);
			$dm = $this->sales_data($mermas);

			//busca ventas de prestashop
			$ps = $this->load->database('prestashop', TRUE);

			/*
			SELECT * FROM `ps_order_detail` WHERE product_id = XXXX
			*/

			$ps->select("o.id_order, o.id_customer, o.date_add, od.product_quantity, c.firstname, c.lastname, o.current_state, osl.name as state");
			$ps->from('ps_order_detail od');
			$ps->join('ps_orders o','o.id_order = od.id_order');
			$ps->join('ps_customer c','c.id_customer = o.id_customer');
			$ps->join('ps_order_state_lang osl','osl.id_order_state = o.current_state');
			$ps->where('od.product_id', $producto['ps_id']);
			$ps->where('o.current_state !=', 6);
			$ps->order_by('o.date_add DESC');
			$ps_orders = $ps->get()->result_array();

			$ps_vendidos = 0;
			foreach ($ps_orders as &$ps_order) {
				$ps_vendidos += $ps_order['product_quantity'];

				$this->db->select('id');
				$this->db->from('clientes');
				$this->db->where('deleted', 0);
				$this->db->where('ps_id', $ps_order['id_customer']);
				$cliente = $this->db->get()->row_array();

				if (!empty($cliente)) {
					$ps_order['id_cliente'] = $cliente['id'];
				}
			}

			// Data gráfico
			//$meses desde la primera compra
			if (!empty($dc['alta'])) {
				$currentTime = strtotime('first day of this month', strtotime($dc['alta']));
			}
			else {
				$currentTime = strtotime('first day of this month', strtotime(date('Ymd')));
			}
			$ultimoMes = date('Ym');

			if ($minTime <= $currentTime) {
				$currentTime = strtotime('first day of this month', $minTime);
			}

			//Busca todos los canales
			$this->db->select('id, nombre, destacado', 0);
			$this->db->from('canales');
			$this->db->where('deleted', 0);
			$this->db->order_by('id', 'ASC');
			$canalesExistentes = $this->db->get()->result_array();

			$canales = [0];
			foreach ($canalesExistentes as $ce) {
				if ($ce['destacado']) {
					$canales[] = $ce['id'];
				}
			}

			//$canales = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

			// Rellena datos incompletos con 0
			$currentMes = date('Ym', $currentTime);
			for($i = 0; $currentMes <= $ultimoMes; $i++) {
				$meses['meses'][$currentMes] = date('m/Y', $currentTime);
				foreach ($canales as $c) {
					$meses['ventas'][$c][$currentMes] = $ventasMeses['ventas'][$c][$currentMes] ?? 0;
				}
				$currentTime = strtotime('+1 month', $currentTime);
				$currentMes = date('Ym', $currentTime);
			}

			// Clientes a la Espera del producto
			$ps->select('m.id_product, m.created, m.customer_email as mail, m.created as fecha, c.id_customer, c.firstname, c.lastname');
			$ps->from('ps_mailalert_customer_oos m');
			$ps->join('ps_customer c','m.id_customer = c.id_customer', 'left');
			$ps->where('m.id_product', $producto['ps_id']);
			$ps->order_by('m.created DESC');
			$producto['ps_alertas'] = $ps->get()->result_array();

			// Historial de precios
			$this->db->from('precios_log');
			$this->db->where('id_producto', $id);
			$this->db->order_by('created', 'DESC');
			$producto['precios'] = $this->db->get()->result_array();

			$producto['fecha_alta'] = $dc['alta'];
			$producto['promedio_costo'] = $dc['promedio'];
			$producto['promedio_pvp'] = $dv['promedio'];
			$producto['total_gastado'] = $dc['total'];
			$producto['total_ganado'] = $dv['total'];
			$producto['saldo'] = $dv['total'] - $dc['total'];
			$producto['utilidad'] = ($dv['promedio'] - $dc['promedio']) * $dv['vendidos'];
			$producto['comprados'] = $dc['comprados'];
			$producto['vendidos'] = $dv['vendidos'];
			$producto['compras'] = $compras;
			$producto['ventas'] = $ventas;
			$producto['devoluciones'] = $devoluciones;
			$producto['devueltos'] = $dd['vendidos'];
			$producto['mermas'] = $mermas;
			$producto['mermados'] = $dm['vendidos'];
			$producto['diferencia'] = $dc['comprados'] - $unidadesEnviadas - $producto['stock'];
			$producto['unidades_reservadas'] = $unidadesReservadas;
			$producto['ventas_proceso'] = $ventas_proceso;
			$producto['unidades_proceso'] = $unidades_proceso;
			$producto['ps_ventas'] = $ps_orders;
			$producto['ps_vendidos'] = $ps_vendidos;
			$producto['otras_ventas'] = $otras_ventas;
			$producto['otros'] = $otros;
			$producto['meses'] = $meses;

			$CI =& get_instance();
			$CI->load->model('almacen_model', 'Almacen');
			$producto['stock_online'] = $this->Almacen->stock_online($id);

			//Busca stock por almacenes 
			$producto['almacenes'] = $this->stock_almacenes($id);
			$producto['stock_correcto'] = $this->valida_stock($id);


			// Busca traspasos del producto
			$this->db->from('productos_traspasos pt');
			$this->db->join('traspasos t', 'pt.id_traspaso = t.id', 'left');
			$this->db->where('pt.id_producto', $id);
			$this->db->where('pt.deleted', 0);
			$this->db->where('t.deleted', 0);
			$this->db->where_in('t.estado_traspaso', [1, 2, 3, 4]);
			$this->db->order_by('t.modified', 'DESC');
			$producto['traspasos'] = $this->db->get()->result_array();


			// Busca inventarios del producto
			$this->db->select('i.*');
			$this->db->from('inventarios i');
			$this->db->where('i.deleted', 0);
			$this->db->where('i.id_producto', $id);
			$this->db->order_by('i.fecha_revision', 'DESC');
			$producto['inventarios'] = $this->db->get()->result_array();

			// Busca Stocks de Market Places
			$producto['marketplaces'] = $this->marketplaces($id);

			return $producto;
		}
	}

	/**
	 * Entrega resumen de un producto para vista detalle
	 * Busca reemplazar full info separada en varias funciones pequeñas
	 */
	public function resumen($id) {
		//$producto = $this->get_basico($id);
		$CI =& get_instance();

		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$this->db->where('deleted', 0);
		$producto = $this->db->get()->row_array();
		$producto['id_image'] =$this->get_ps_image($producto['ps_id']);
		$producto['imagenes'] =$this->get_ps_images($producto['ps_id']);

		$producto['ps_state'] = $this->ps_state($producto['ps_id']);
		$producto['ps_stock'] = $this->ps_stock($producto['ps_id']);

		$CI->load->model('almacen_model', 'Almacen');
		$producto['stock_online'] = $this->Almacen->stock_online($id);

		// Última compra
		$this->db->select('cp.costo_neto, c.descuenta_iva, c.fecha');
		$this->db->from('compras_productos cp');
		$this->db->join('compras c', 'cp.id_compra = c.id', 'inner');
		$this->db->where('cp.id_producto', $id);
		$this->db->where('cp.deleted', 0);
		$this->db->where('c.deleted', 0);
		$this->db->order_by('c.fecha', 'DESC');
		$this->db->limit(1);

		$ultima_compra = $this->db->get()->row_array();

		// Primera compra
		$this->db->select('c.fecha');
		$this->db->from('compras_productos cp');
		$this->db->join('compras c', 'cp.id_compra = c.id', 'inner');
		$this->db->where('cp.id_producto', $id);
		$this->db->where('cp.deleted', 0);
		$this->db->where('c.deleted', 0);
		$this->db->order_by('c.fecha', 'ASC');
		$this->db->limit(1);

		$primera_compra = $this->db->get()->row_array();

		// Normalización
		$producto['fecha_alta'] = $primera_compra['fecha'] ?? null;
		$producto['ultima_compra'] = $ultima_compra['fecha'] ?? null;

		// Costo bruto
		$producto['costo_bruto'] = null;
		if (!empty($ultima_compra['costo_neto'])) {
			$factor = !empty($ultima_compra['descuenta_iva']) ? 1.19 : 1;
			$producto['costo_bruto'] = $ultima_compra['costo_neto'] * $factor;
		}

		$producto['ps_price'] = $this->ps_prices($producto['ps_id'], 1);

		$ps = $this->load->database('prestashop', TRUE);
		// Clientes a la Espera del producto
		$ps->select('COUNT(*) AS total');
		$ps->from('ps_mailalert_customer_oos m');
		$ps->where('m.id_product', $producto['ps_id']);
		$producto['ps_alertas'] = (int) $ps->get()->row()->total;

		// Fecha inicio: primer día del mes actual - 5 meses
		$fecha_inicio = date('Y-m-01', strtotime('-5 months'));

		$this->db->select("
			v.id_canal,
			DATE_FORMAT(v.fecha_venta, '%Y%m') AS mes,
			SUM(pv.cantidad) AS unidades
		");
		$this->db->from('productos_ventas pv');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'inner');
		$this->db->join('canales c', 'v.id_canal = c.id', 'inner');
		$this->db->where('pv.id_producto', $id);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where('c.deleted', 0);
		$this->db->where('c.destacado', 1);
		$this->db->where('v.fecha_venta >=', $fecha_inicio);
		$this->db->group_by(['v.id_canal', 'mes']);
		$this->db->order_by('mes', 'ASC');

		$ventas_6_meses = $this->db->get()->result_array();

		// Inicializa estructura final
		$producto['meses'] = ['meses'=>[], 'ventas'=>[]];

		// Base: primer día del mes actual
		$base = date('Y-m-01');

		// Últimos 6 meses (YYYYMM)
		for ($i = 0; $i < 6; $i++) {
			$mes = date('Ym', strtotime($base . " -$i months"));
			$producto['meses']['meses'][$mes] = $mes;
		}

		ksort($producto['meses']['meses']);

		// Canales activos destacados
		$canales = $this->db->select('id')
			->from('canales')
			->where('deleted', 0)
			->where('destacado', 1)
			->get()->result_array();

		// Inicializa totales (canal 0)
		foreach ($producto['meses']['meses'] as $mes) {
			$producto['meses']['ventas'][0][$mes] = 0;
		}

		// Inicializa cada canal con 0
		foreach ($canales as $c) {
			foreach ($producto['meses']['meses'] as $mes) {
				$producto['meses']['ventas'][$c['id']][$mes] = 0;
			}
		}

		// Carga ventas reales y suma totales
		foreach ($ventas_6_meses as $v) {
			if (isset($producto['meses']['ventas'][$v['id_canal']][$v['mes']])) {
				$producto['meses']['ventas'][$v['id_canal']][$v['mes']] += (int)$v['unidades'];
				$producto['meses']['ventas'][0][$v['mes']] += (int)$v['unidades'];
			}
		}

		$producto['marketplaces'] = $this->marketplaces($id);

		$producto['almacenes'] = $this->stock_almacenes($id);
		$producto['stock_correcto'] = $this->valida_stock($id);

		// Cuenta mermas
		$this->db->select('SUM(pv.cantidad) AS total_mermados');
		$this->db->from('productos_ventas pv');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'inner');
		$this->db->where('pv.id_producto', $id);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where('v.id_tipo_venta', 4); // solo mermas

		$resultado = $this->db->get()->row_array();

		$producto['mermados'] = $resultado['total_mermados'] ?? 0;


		return $producto;
	}

	/**
	 * Entrega las ventas del producto filtradas y paginadas
	 */
	public function detalle_ventas($id, $filtros){
		// Consulta única: todas las ventas del producto
		$this->db->select('pv.*, v.*, v.fecha as v_fecha, c.nombre AS cliente_nombre, c.email AS cliente_email')
				->from('productos_ventas pv')
				->join('ventas v', 'pv.id_venta = v.id', 'left')
				->join('clientes c', 'v.id_cliente = c.id', 'left')
				->where('pv.id_producto', $id)
				->where('pv.deleted', 0)
				->where('v.deleted', 0)
				->order_by('v.fecha_venta', 'DESC');

		if (isset($filtros['vendido'])) {
			$this->db->where('vendido', $filtros['vendido']);
		}
		if (isset($filtros['reservado'])) {
			$this->db->where('reservado', $filtros['reservado']);
		}
		if (isset($filtros['id_tipo_venta'])) {
			$this->db->where_in('id_tipo_venta', $filtros['id_tipo_venta']);
		}

		if (!empty($filtros['id_canal'])) {
			$this->db->where('id_canal', $filtros['id_canal']);
		}
		if (!empty($filtros['id_transportista'])) {
			$this->db->where('id_transportista', $filtros['id_transportista']);
		}

		$result['total'] = $this->db->count_all_results('', false);

		// Condiciones de Paginación
		$this->db->limit($filtros['cantidad'], $filtros['offset']);

		$ventas = $this->db->get()->result_array();

		// Mapear productos solo una vez
		$ventas_ids = array_column($ventas, 'id');
		$productos_map = [];
		if (!empty($ventas_ids)) {
			$this->db->select('pv.id_venta, p.id, p.nombre, pv.cantidad')
					->from('productos_ventas pv')
					->join('productos p', 'pv.id_producto = p.id', 'left')
					->where_in('pv.id_venta', $ventas_ids)
					->where('pv.deleted', 0);;
			$productos = $this->db->get()->result_array();
			foreach ($productos as $p) {
				$productos_map[$p['id_venta']][] = [
					'id'       => $p['id'],
					'nombre'   => $p['nombre'],
					'cantidad' => $p['cantidad']
				];
			}
		}

		// Clasificar ventas y calcular variables
		foreach ($ventas as &$v) {
			// Carga Productos
			$v['productos'] = $productos_map[$v['id']] ?? [];

			// Estado PS
			if ($v['ps_id'] != 0) $v['ps_state'] = $this->Venta->get_order_state($v['ps_id']);

		}

		$result['ventas'] = $ventas;

		return $result;
	}

	/**
	 * Entrega las ventas del producto filtradas y paginadas
	 */
	public function traspasos($id, $filtros){

		$this->db->from('productos_traspasos pt');
		$this->db->join('traspasos t', 'pt.id_traspaso = t.id', 'left');
		$this->db->where('pt.id_producto', $id);
		$this->db->where('pt.deleted', 0);
		$this->db->where('t.deleted', 0);
		$this->db->where_in('t.estado_traspaso', [1, 2, 3, 4]);
		$this->db->order_by('t.modified', 'DESC');

		if (isset($filtros['id_origen'])) {
			$this->db->where('id_origen', $filtros['id_origen']);
		}
		if (isset($filtros['id_destino'])) {
			$this->db->where('id_destino', $filtros['id_destino']);
		}

		$result['total'] = $this->db->count_all_results('', false);

		// Condiciones de Paginación
		$this->db->limit($filtros['cantidad'], $filtros['offset']);

		$result['traspasos'] = $this->db->get()->result_array();

		return $result;
	}

	/**
	 * Entrega los inventarios realizados al producto filtrados y paginados
	 */
	public function inventarios($id, $filtros){

		// Busca inventarios del producto
		$this->db->select('i.*');
		$this->db->from('inventarios i');
		$this->db->where('i.deleted', 0);
		$this->db->where('i.id_producto', $id);
		$this->db->order_by('i.fecha_revision', 'DESC');

		if (isset($filtros['id_almacen'])) {
			$this->db->where('id_almacen', $filtros['id_almacen']);
		}

		$result['total'] = $this->db->count_all_results('', false);

		// Condiciones de Paginación
		$this->db->limit($filtros['cantidad'], $filtros['offset']);

		$result['inventarios'] = $this->db->get()->result_array();

		return $result;
	}

	public function compras($id, $filtros) {
		$this->db->select('cp.*, c.id_proveedor, c.fecha_recepcion, c.id_tipo_compra, c.descuenta_iva, c.folio');
		$this->db->from('compras_productos cp');
		$this->db->join('compras c', 'cp.id_compra = c.id', 'left');
		$this->db->where('cp.id_producto', $id);
		$this->db->where('cp.deleted', 0);
		$this->db->where('c.deleted', 0);
		$this->db->order_by('c.fecha', 'DESC');

		$result['total'] = $this->db->count_all_results('', false);

		// Condiciones de Paginación
		$this->db->limit($filtros['cantidad'], $filtros['offset']);

		$result['compras'] = $this->db->get()->result_array();

		return $result;
	}

	/**
	 * Busca ventas de PS por el ps-id del producto
	 */
	public function ventas_ps($ps_id, $filtros) {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("o.id_order, o.id_customer, o.date_add, od.product_quantity, c.firstname, c.lastname, o.current_state, osl.name as state");
		$ps->from('ps_order_detail od');
		$ps->join('ps_orders o','o.id_order = od.id_order');
		$ps->join('ps_customer c','c.id_customer = o.id_customer');
		$ps->join('ps_order_state_lang osl','osl.id_order_state = o.current_state');
		$ps->where('od.product_id', $ps_id);
		$ps->where('o.current_state !=', 6);
		$ps->order_by('o.date_add DESC');

		$result['total'] = $ps->count_all_results('', false);

		// Condiciones de Paginación
		$ps->limit($filtros['cantidad'], $filtros['offset']);

		$result['ventas_ps'] = $ps->get()->result_array();


		foreach ($result['ventas_ps'] as &$ps_order) {
			//$ps_vendidos += $ps_order['product_quantity'];

			$this->db->select('id, nombre');
			$this->db->from('clientes');
			$this->db->where('deleted', 0);
			$this->db->where('ps_id', $ps_order['id_customer']);
			$cliente = $this->db->get()->row_array();

			if (!empty($cliente)) {
				$ps_order['id_cliente'] = $cliente['id'];
				$ps_order['cliente'] = $cliente['nombre'];
			}
		}

		return $result;
	}

	public function precios($id, $filtros) {
		// Historial de precios
		$this->db->from('precios_log');
		$this->db->where('id_producto', $id);
		$this->db->order_by('created', 'DESC');

		$result['total'] = $this->db->count_all_results('', false);

		// Condiciones de Paginación
		$this->db->limit($filtros['cantidad'], $filtros['offset']);

		$result['precios'] = $this->db->get()->result_array();

		foreach ($result['precios'] as &$precio) {
			$precio['descuento_anterior'] = $precio['precio_normal_anterior'] - $precio['precio_final_anterior'];
			$precio['descuento'] = $precio['precio_normal'] - $precio['precio_final'];
		}

		return $result;
	}


	public function suscritos($ps_id, $filtros) {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select('m.id_product, m.created, m.customer_email as mail, m.created as fecha, c.id_customer, c.firstname, c.lastname');
		$ps->from('ps_mailalert_customer_oos m');
		$ps->join('ps_customer c','m.id_customer = c.id_customer', 'left');
		$ps->where('m.id_product', $ps_id);
		$ps->order_by('m.created DESC');

		$result['total'] = $ps->count_all_results('', false);

		// Condiciones de Paginación
		$ps->limit($filtros['cantidad'], $filtros['offset']);

		$result['suscritos'] = $ps->get()->result_array();

		foreach ($result['suscritos'] as &$suscrito) {
			//$ps_vendidos += $suscrito['product_quantity'];

			$this->db->select('id, nombre');
			$this->db->from('clientes');
			$this->db->where('deleted', 0);
			$this->db->where('ps_id', $suscrito['id_customer']);
			$cliente = $this->db->get()->row_array();

			if (!empty($cliente)) {
				$suscrito['id_cliente'] = $cliente['id'];
				$suscrito['cliente'] = $cliente['nombre'];
			}
		}


		return $result;
	}

	public function lista() {
		$this->db->select('id, nombre, id_proveedor');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('nombre', 'ASC');
		$query = $this->db->get();

		$productos = $query->result_array();

		$CI =& get_instance();
		$CI->load->model('proveedor_model', 'Proveedor');
		$proveedores = $this->Proveedor->lista();

		$r = [];
		foreach ($productos as $p) {
			$r[$p['id']] = $p['nombre'] . ' [' . $proveedores[$p['id_proveedor']] . ']';
		}

		return $r;
	}

	/**
	 * Entrega lista de codigos de barras
	 */
	public function codigos_barras() {
		$this->db->select('id, codigo_barra');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->where('codigo_barra !=', 0);
		$this->db->order_by('id', 'ASC');
		$productos = $this->db->get()->result_array();;

		$r = [];
		foreach ($productos as $p) {
			$r[$p['codigo_barra']] = $p['id'];
		}

		return $r;
	}

	/**
	 * Entrega id y precio ordenado usando por indice el nombre
	 */
	public function get_nombres() {
		$productos = $this->Producto->list_precios();
		foreach ($productos as $idProducto => $producto) {
			$tmp_productos[$producto['nombre']] = [
				'id' => $idProducto,
				'precio_normal' => $producto['precio_normal'],
				'descuento' => $producto['descuento'],
				'precio' => $producto['precio'],
				'descuento_tienda' => $producto['descuento_tienda'],
				'precio_tienda' => $producto['precio_tienda'],
				'disponibles' => $producto['disponibles']
			];
		}

		return $tmp_productos;
	}


	public function list_info(){
		// Busca precios en Ventas
		$this->db->select('id, nombre, precio, ps_id, disponibles');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('nombre', 'ASC');
		$query = $this->db->get();

		$productos = $query->result_array();

		$r = [];
		foreach ($productos as $p) {
			$r[$p['id']] = [
				'nombre' => $p['nombre'],
				'disponibles' => $p['disponibles']
			];
		}
		return $r;
	}

	public function list_precios() {
		// Busca precios en Ventas
		$this->db->select('id, nombre, precio, ps_id, disponibles');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('nombre', 'ASC');
		$query = $this->db->get();

		$productos = $query->result_array();

		$ps_prices = $this->ps_prices(0, 1);

		$CI =& get_instance();
		$CI->load->model('almacen_model', 'Almacen');
		$stocks = $this->Almacen->stock_productos();

		$r = [];
		foreach ($productos as $p) {
			foreach($stocks[$p['id']] as $idAlmacen => $stockAlmacen){
				$disponiblesAlmacen[$idAlmacen] = $stockAlmacen['disponibles'];
			}

			$r[$p['id']] = [
				'nombre' => $p['nombre'],
				'precio_normal' => $ps_prices[$p['ps_id']]['precio_normal'] ?? $p['precio'],
				'descuento' => $ps_prices[$p['ps_id']]['descuento'] ?? 0,
				'precio' => $ps_prices[$p['ps_id']]['precio'] ?? $p['precio'],
				'descuento_tienda' => $ps_prices[$p['ps_id']]['descuento_tienda'] ?? 0,
				'precio_tienda' => $ps_prices[$p['ps_id']]['precio_tienda'] ?? 0,
				'disponibles' => $p['disponibles'],
				'disponibles_almacen' => $disponiblesAlmacen,

				/* Comentado 02/02/2024
				'disponibles_tienda' => $stocks[$p['id']][2]['disponibles'],
				'disponibles_bodega' => $stocks[$p['id']][1]['disponibles']
				*/
			];
		}
		return $r;
	}

	public function list_costos() {
		$this->db->select('id, nombre, costo_neto');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('nombre', 'ASC');
		$query = $this->db->get();

		$productos = $query->result_array();

		$r = [];
		foreach ($productos as $p) {
			$r[$p['id']] = [
				'nombre' => $p['nombre'],
				'costo_neto' => $p['costo_neto']
			];
		}

		return $r;
	}

	/**
	 * Entrega un listado con los productos peor vendidos, en 3 categorias:
	 * 3 meses, 6 meses y 12 meses sin ventas
	 */
	public function worst() {
		// Insumos, Demos, Virtuales
		$categoriasIgnoradas = [7, 10, 11];

		//busca fecha de hace 3 meses
		$unMes = date("Y-m-d",strtotime(date("Y-m-d") . "- 1 month"));
		$dosMeses = date("Y-m-d",strtotime(date("Y-m-d") . "- 2 months"));
		$tresMeses = date("Y-m-d",strtotime(date("Y-m-d") . "- 3 months"));
		$seisMeses = date("Y-m-d",strtotime(date("Y-m-d") . "- 6 months"));
		$doceMeses = date("Y-m-d",strtotime(date("Y-m-d") . "- 12 months"));

		//Buscar ventas en que ultima venta esté entre 1 y 2 meses
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_venta <', $unMes);
		$this->db->where('ultima_venta >', $dosMeses);
		$this->db->where('ultima_compra <', $dosMeses);
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');
		$ventasUnMes = $this->db->get()->result_array();

		//Productos nunca vendidos o reposición
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_compra <', $unMes);
		$this->db->where('ultima_compra >=', $dosMeses);

		$this->db->group_start();
			$this->db->or_where('ultima_venta IS NULL');
			$this->db->or_where('ultima_venta <', $unMes);
		$this->db->group_end();

		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');

		$ventasUnMes = array_merge($ventasUnMes, $this->db->get()->result_array());


		//Buscar ventas en que ultima venta esté entre 2 y 3 meses
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_venta <=', $dosMeses);
		$this->db->where('ultima_venta >', $tresMeses);
		$this->db->where('ultima_compra <', $tresMeses);
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');
		$ventasDosMeses = $this->db->get()->result_array();

		//Productos nunca vendidos
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_compra <=', $dosMeses);
		$this->db->where('ultima_compra >', $tresMeses);

		$this->db->group_start();
			$this->db->or_where('ultima_venta IS NULL');
			$this->db->or_where('ultima_venta <', $dosMeses);
		$this->db->group_end();

		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');

		$ventasDosMeses = array_merge($ventasDosMeses, $this->db->get()->result_array());


		//Buscar ventas en que ultima venta esté entre 3 y 6 meses
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_venta <=', $tresMeses);
		$this->db->where('ultima_venta >', $seisMeses);
		$this->db->where('ultima_compra <', $seisMeses);
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');
		$ventasTresMeses = $this->db->get()->result_array();

		//Productos nunca vendidos
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_compra <=', $tresMeses);
		$this->db->where('ultima_compra >', $seisMeses);

		$this->db->group_start();
			$this->db->or_where('ultima_venta IS NULL');
			$this->db->or_where('ultima_venta <', $tresMeses);
		$this->db->group_end();

		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');

		$ventasTresMeses = array_merge($ventasTresMeses, $this->db->get()->result_array());


		// Buscar ventas en que ultima venta esté entre 6 y 12 meses
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_venta <=', $seisMeses);
		$this->db->where('ultima_venta >', $doceMeses);
		$this->db->where('ultima_compra <', $doceMeses);
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');
		$ventasSeisMeses = $this->db->get()->result_array();

		//Productos nunca vendidos
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_compra <=', $seisMeses);
		$this->db->where('ultima_compra >', $doceMeses);
		
		$this->db->group_start();
			$this->db->or_where('ultima_venta IS NULL');
			$this->db->or_where('ultima_venta <', $seisMeses);
		$this->db->group_end();

		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');

		$ventasSeisMeses = array_merge($ventasSeisMeses, $this->db->get()->result_array());

		// Buscar ventas en que ultima venta sea anterior a 12 meses
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_venta <=', $doceMeses);
		$this->db->where('ultima_compra <=', $doceMeses);
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');
		$ventasDoceMeses = $this->db->get()->result_array();

		//Productos nunca vendidos
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('ultima_compra <=', $doceMeses);
		$this->db->where('ultima_venta IS NULL');
		$this->db->where('stock >', 0);
		$this->db->where('deleted', 0);
		$this->db->where_not_in('id_categoria', $categoriasIgnoradas);
		$this->db->order_by('stock DESC');

		$ventasDoceMeses = array_merge($ventasDoceMeses, $this->db->get()->result_array());

		$data['un_mes']['productos'] = $ventasUnMes;
		$data['dos_meses']['productos'] = $ventasDosMeses;
		$data['tres_meses']['productos'] = $ventasTresMeses;
		$data['seis_meses']['productos'] = $ventasSeisMeses;
		$data['doce_meses']['productos'] = $ventasDoceMeses;

		$data['un_mes']['nombre'] = 'Productos no vendidos en 1 mes';
		$data['dos_meses']['nombre'] = 'Productos no vendidos en 2 meses';
		$data['tres_meses']['nombre'] = 'Productos no vendidos en 3 meses';
		$data['seis_meses']['nombre'] = 'Productos no vendidos en 6 meses';
		$data['doce_meses']['nombre'] = 'Productos no vendidos en 12 meses';

		return $data;
	}

	/**
	 * Entrega los totales por proveedor de sus productos
	 * @param array con productos agrupados por proveedor
	 */
	public function totales_proveedor($productos, $compras){
		$totales = [];
		$cTotal = 0;
		$totales[0]['compras'] = 0;
		$totales[0]['costo_stock'] = 0;
		$totales[0]['pvp_stock'] = 0;
		$totales[0]['valor_ventas'] = 0;
		$totales[0]['utilidad_ventas'] = 0;

		foreach ($productos as $k => $proveedor) {
			$totales[$k]['costo_stock'] = 0;
			$totales[$k]['pvp_stock'] = 0;
			$totales[$k]['valor_ventas'] = 0;
			foreach ($proveedor as $p) {
				$totales[$k]['costo_stock'] += $p['stock'] * $p['costo'];
				$totales[$k]['pvp_stock'] += $p['stock'] * $p['precio'];
				$totales[$k]['valor_ventas'] += $p['total_vendido'];
			}
			if (!empty($compras[$k])) {
				$totales[$k]['compras'] = $compras[$k];
				$totales[$k]['costo_ventas'] = $compras[$k] - $totales[$k]['costo_stock'];
				$totales[$k]['utilidad_ventas'] = $totales[$k]['valor_ventas'] - $totales[$k]['costo_ventas'];
			}
			else {
				$totales[$k]['compras'] = 0;
				$totales[$k]['costo_ventas'] = 0 - $totales[$k]['costo_stock'];
				$totales[$k]['utilidad_ventas'] = $totales[$k]['valor_ventas'] - $totales[$k]['costo_ventas'];
			}
			$totales[0]['compras'] += $totales[$k]['compras'];
			$totales[0]['costo_stock'] += $totales[$k]['costo_stock'];
			$totales[0]['pvp_stock'] += $totales[$k]['pvp_stock'];
			$totales[0]['valor_ventas'] += $totales[$k]['valor_ventas'];
			$totales[0]['utilidad_ventas'] += $totales[$k]['utilidad_ventas'];
		}

		return $totales;
	}

		/**
	 * Busca productos que no estén relacionados a un producto de prestashop
	 */
	public function relation() {
		$this->db->select('id, nombre');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->where('ps_id', 0);
		$this->db->order_by('created', 'DESC');
		$productos = $this->db->get()->result_array();

		return $productos;
	}

	/**
	 * Devuelve los productos ordenado por ventas
	 * $t = orden por unidades o monto total
	 * $m = Periodo
	 * $c = id Canal
	 * $p = id proveedor
	 */
	public function ventas($t, $m, $c, $p, $ct, $offset = 0, $cantidad = 100) {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		// --- 1. Obtener costos de productos ---
		$costos = $this->get_costo_neto_producto(0, $m, 0);


		// --- 2. Query de ventas con filtros directos en JOIN ---
		$this->db->select('
			pv.id_producto,
			SUM(pv.cantidad) as cantidad,
			SUM(pv.precio_total) as total,
			p.id,
			p.nombre,
			p.id_ranking,
			p.stock,
			p.ps_id,
			sa.physical_quantity as ps_stock,
			p.disponibles,
			sa.quantity as ps_disponibles,
			pl.available_now,
			pl.available_later
		');
		$this->db->from('productos_ventas pv');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'left');
		$this->db->join('productos p', 'pv.id_producto = p.id', 'inner');
		$this->db->join($dbps.'.ps_stock_available sa', 'p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps.'.ps_product_lang pl', 'p.ps_id = pl.id_product', 'left');

		// --- Filtros ---
		$this->db->where('p.deleted', 0);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where('v.id_estado_venta', 5);
		$this->db->where('v.id_tipo_venta', 1);

		if (!empty($p)) { // Proveedor
			$this->db->where('p.id_proveedor', $p);
		}
		if (!empty($ct)) { // Categoría
			$this->db->where('p.id_categoria', $ct);
		}
		if ($c != 0) { // Canal
			$this->db->where('v.id_canal', $c);
		}

		// --- Filtro por periodo ---
		if (strlen($m) >= 4) {
			$ano = substr($m, 0, 4);
			$this->db->where('YEAR(v.fecha_venta)', $ano);

			if (strlen($m) == 6) {
				$mes = substr($m, 4, 2);
				$this->db->where('MONTH(v.fecha_venta)', $mes);
			}
		}

		// Agrupación por producto
		$this->db->group_by('pv.id_producto');

		// Orden principal + secundario estable
		$this->db->order_by(($t == 1) ? 'cantidad' : 'total', 'desc');
		$this->db->order_by('pv.id_producto', 'asc');

		// --- 3. Obtener todos los resultados ---
		$productos_ventas_all = $this->db->get()->result_array();
		$ventas_total = count($productos_ventas_all);

		// --- 4. Calcular totales globales ---
		$montoTotal = 0;
		$cantidadTotal = 0;
		$costoNetoTotal = 0;

		foreach ($productos_ventas_all as &$pv) {
			$pid = $pv['id'];
			$costo_unit = $costos[$pid] ?? 0;

			$pv['costo_neto'] = $pv['cantidad'] * $costo_unit;

			$montoTotal += $pv['total'];
			$cantidadTotal += $pv['cantidad'];
			$costoNetoTotal += $pv['costo_neto'];
		}

		// --- 5. Paginar manualmente ---
		$productos_ventas_page = array_slice($productos_ventas_all, $offset, $cantidad);

		// --- 6. Preparar resultados para la vista ---
		$tmp_p = [];
		foreach ($productos_ventas_page as $pv) {
			// Mantener la clave original `id` para compatibilidad
			$tmp_p[$pv['id']] = $pv;
		}

		// --- 7. Agregar marketplaces solo si hay productos ---
		if (!empty($tmp_p)) {
			$tmp_p = $this->agregarMarketplaces($tmp_p);
		}

		// --- 8. Retornar datos y totales ---
		return [
			'total_registros' => $ventas_total,
			'monto' => $montoTotal,
			'monto_neto' => round($montoTotal / 1.19),
			'monto_iva' => $montoTotal - round($montoTotal / 1.19),
			'cantidad' => $cantidadTotal,
			'costo_neto' => $costoNetoTotal,
			'costo_bruto' => $costoNetoTotal * 1.19,
			'costo_iva' => ($costoNetoTotal * 1.19) - $costoNetoTotal,
			'margen' => $montoTotal > 0 
				? round(100 - (($costoNetoTotal * 1.19) * 100 / $montoTotal), 2)
				: 0,
			'data' => $tmp_p
		];
	}


	public function get_costo_neto($id) {
		$this->db->select('costo_neto');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$p = $this->db->get()->row_array();

		if (!empty($p['costo_neto'])) {
			return $p['costo_neto'];
		}

		return 0;
	}

	public function get_costo_neto_producto($idProducto = null, $anioMes, $debug = 0) {
		// Último día del mes en formato YYYY-MM-DD
		$fechaVenta = date('Y-m-t', strtotime($anioMes . '01'));

		// Escapar valores para evitar problemas de SQL
		$fechaVentaEscaped = $this->db->escape($fechaVenta);

		// Si estamos consultando un solo producto
		if ($idProducto) {
			$idProductoEscaped = (int)$idProducto;

			// 1. Última compra ANTES o en la fecha
			$sql = "
				SELECT cp.costo_neto
				FROM compras_productos cp
				JOIN compras c ON cp.id_compra = c.id
				WHERE cp.id_producto = {$idProductoEscaped}
				AND c.fecha <= {$fechaVentaEscaped}
				AND cp.deleted = 0
				AND c.deleted = 0
				ORDER BY c.fecha DESC, cp.id DESC
				LIMIT 1
			";
			$result = $this->db->query($sql)->row_array();
			if (!empty($result)) return $result['costo_neto'];

			// 2. Primera compra FUTURA
			$sql = "
				SELECT cp.costo_neto
				FROM compras_productos cp
				JOIN compras c ON cp.id_compra = c.id
				WHERE cp.id_producto = {$idProductoEscaped}
				AND c.fecha > {$fechaVentaEscaped}
				AND cp.deleted = 0
				AND c.deleted = 0
				ORDER BY c.fecha ASC, cp.id ASC
				LIMIT 1
			";
			$result = $this->db->query($sql)->row_array();
			return empty($result) ? 0 : $result['costo_neto'];
		}

		// -----------------------------------------------------------------
		// Si estamos consultando TODOS los productos
		// -----------------------------------------------------------------

		// 1. Última compra ANTES o en la fecha
		$sql = "
			SELECT cp.id_producto, cp.costo_neto
			FROM compras_productos cp
			INNER JOIN (
				SELECT cp2.id_producto, MAX(c2.fecha) AS max_fecha
				FROM compras_productos cp2
				JOIN compras c2 ON cp2.id_compra = c2.id
				WHERE c2.fecha <= {$fechaVentaEscaped}
				AND cp2.deleted = 0
				AND c2.deleted = 0
				GROUP BY cp2.id_producto
			) ult ON ult.id_producto = cp.id_producto
			JOIN compras c ON cp.id_compra = c.id AND c.fecha = ult.max_fecha
			WHERE cp.deleted = 0 AND c.deleted = 0
		";

		$productosConCosto = [];
		foreach ($this->db->query($sql)->result_array() as $row) {
			$productosConCosto[$row['id_producto']] = $row['costo_neto'];
		}

		// 2. Primera compra FUTURA (solo para los que no tienen costo aún)
		$sql = "
			SELECT cp.id_producto, cp.costo_neto
			FROM compras_productos cp
			INNER JOIN (
				SELECT cp2.id_producto, MIN(c2.fecha) AS min_fecha
				FROM compras_productos cp2
				JOIN compras c2 ON cp2.id_compra = c2.id
				WHERE c2.fecha > {$fechaVentaEscaped}
				AND cp2.deleted = 0
				AND c2.deleted = 0
				GROUP BY cp2.id_producto
			) fut ON fut.id_producto = cp.id_producto
			JOIN compras c ON cp.id_compra = c.id AND c.fecha = fut.min_fecha
			WHERE cp.deleted = 0 AND c.deleted = 0
		";

		foreach ($this->db->query($sql)->result_array() as $row) {
			if (!isset($productosConCosto[$row['id_producto']])) {
				$productosConCosto[$row['id_producto']] = $row['costo_neto'];
			}
		}

		// 3. Completar con 0 los productos que nunca tuvieron compras
		$productosTotales = $this->db->query("SELECT id FROM productos")->result_array();
		foreach ($productosTotales as $producto) {
			if (!isset($productosConCosto[$producto['id']])) {
				$productosConCosto[$producto['id']] = 0;
			}
		}

		return $productosConCosto;
	}

	/**
	 * Entrega listado de prodcutos con unidades prevendidas
	 */
	public function presale() {
		//Buscar ventas en estados de preventa
		/*
			SELECT p.id, p.nombre, SUM(pv.cantidad) prevendidos
			FROM productos p
			LEFT JOIN productos_ventas pv ON p.id = pv.id_producto
			LEFT JOIN ventas v ON pv.id_venta = v.id
			WHERE v.id_estado_venta IN (9, 12, 19)
			GROUP BY p.id, p.nombre
			ORDER BY p.nombre ASC
		*/
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('p.id, p.nombre, SUM(pv.cantidad) as prevendidos, p.disponibles, p.costo, p.id_proveedor, ps.available_date');
		$this->db->from('productos p');
		$this->db->join('productos_ventas pv', 'p.id = pv.id_producto', 'left');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'left');
		$this->db->join($dbps.'.ps_product_shop ps','ON p.ps_id = ps.id_product', 'left');
		$this->db->where_in('v.id_estado_venta', [9, 12, 19, 20]);
		$this->db->where('p.deleted', 0);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->group_by('p.id, p.nombre, p.disponibles');
		$this->db->order_by('p.id_proveedor', 'asc');
		$this->db->order_by('p.disponibles', 'asc');
		$productos = $this->db->get()->result_array();

		$costoTotal = 0;
		$costos = [];
		foreach ($productos as $p) {
			if ($p['disponibles'] < 0) {
				$costos[$p['id']] = $p['prevendidos'] * $p['costo'];
				if (empty($costos['proveedores'][$p['id_proveedor']])) {
					$costos['proveedores'][$p['id_proveedor']] = $costos[$p['id']];
				}
				else {
					$costos['proveedores'][$p['id_proveedor']] += $costos[$p['id']];
				}
				$costoTotal += $costos[$p['id']];
			}
		}
		$costos['total'] = $costoTotal;
		$return['productos'] = $productos;
		$return['costos'] = $costos;

		return $return;
	}

	/**
	 * Entrega Listado de productos segun estado
	 */
	public function get_by_state($idEstado) {
		if (!empty($idEstado)) {
			$estados = [$idEstado];
		}
		else {
			$estados = [9, 10, 12, 19, 20];
		}

		$this->db->select('p.id, p.nombre, SUM(pv.cantidad) as prevendidos, p.disponibles, p.id_proveedor');
		$this->db->from('productos p');
		$this->db->join('productos_ventas pv', 'p.id = pv.id_producto', 'left');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'left');
		$this->db->where_in('v.id_estado_venta', $estados);
		$this->db->where('p.deleted', 0);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->group_by('p.id, p.nombre, p.disponibles');
		$this->db->order_by('p.nombre', 'asc');

		$productos = $this->db->get()->result_array();

		return $productos;
	}

	/**
	 * Entrega Listado de productos agotados en el ultimo mes ordenados por fecha desc
	 */
	public function agotados($p = 0) {
		$unMes = date("Y-m-d",strtotime(date("Y-m-d") . "- 1 month"));

		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('p.id, p.nombre, p.ps_id, p.disponibles, p.id_proveedor, p.ultima_venta, p.id_ranking, sa.quantity');
		$this->db->from('productos p');
		$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->where('p.disponibles', 0);
		$this->db->where('ultima_venta >=', $unMes);
		if ($p) {
			$this->db->where('p.id_proveedor', $p);
		}
		$this->db->order_by('p.ultima_venta', 'DESC');
		$productos = $this->db->get()->result_array();
		$productos = $this->agregarMarketplaces($productos);

		return $productos;
	}


	public function list_rankings() {
		$this->db->select('id, nombre');
		$this->db->from('rankings');
		$this->db->where('deleted', 0);
		$this->db->order_by('id', 'ASC');
		$rankings = $this->db->get()->result_array();

		foreach ($rankings as $v) {
			$r[$v['id']] = $v['nombre'];
		}

		return $r;
	}


	/**
	 * Entrega tres listados de productos para Administrar Mercado Libre
	 * Productos publicados en Mercado libre, indicando stock disponible y stock publicado en ML
	 * Productos no publicados en Mercado libre, mostrando stock y ordenados por stock desc
	 * Prodcutos sin stock y no publicados en Mercado libre
	 */
	public function get_meli(){
		// Obtiene todos los productos del sistema
		$this->db->select('*');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('disponibles', 'DESC');
		$productos = $this->db->get()->result_array();

		$publicados = [
			1 => [],
			2 => [],
			3 => [],
			4 => []
		];
		$noPublicados = $publicados; 
		$agotados = $publicados;
		// Los organiza en 3 arreglos: Publicados, No publicados y agotados.
		foreach ($productos as $p){
			if ($p['activo_meli']) {
				$publicados[$p['id_categoria']][] = $p;
			}
			else if ($p['disponibles'] > 0) {
				$noPublicados[$p['id_categoria']][] = $p;
			}
			else {
				$agotados[$p['id_categoria']][] = $p;
			}
		}

		$data = [
			'publicados' => $publicados,
			'no_publicados' => $noPublicados,
			'agotados' => $agotados
		];
		
		// Retorna arreglo con los 3 listados
		return $data;
	}

	public function resumen_marketplace($canal, $ini = '', $fin = '') {
		$data = [];
	
		$ini = $ini ?: date('Y-m-d', strtotime('-30 days'));
		$fin = $fin ?: date('Y-m-d');
	
		$data30 = $this->contar_ventas($canal, $ini, $fin);
		$data['ventas30'] = $data30['ventas'];
		$data['facturado30'] = $data30['facturado'];

		$ini60 = date('Y-m-d', strtotime('-60 days'));
		$fin60 = date('Y-m-d');
		$data60 = $this->contar_ventas($canal, $ini60, $fin60);
		$data['ventas60'] = $data60['ventas'];
		$data['facturado60'] = $data60['facturado'];

		$idsVentas = $this->get_ids_ventas($canal, $ini, $fin);
		$noFisicos = [7, 10, 11];
	
		$data['top_productos'] = !empty($idsVentas) ? $this->top_productos($idsVentas, $noFisicos) : [];
		$data['top_categorias'] = !empty($idsVentas) ? $this->top_categorias($idsVentas, $noFisicos) : [];
	
		$data['semanas'] = $this->ventas_por_semana($canal);

		$data['disponibles'] = $this->contar_productos([
			'mp.activo' => 1, // Activo en marketplaces_productos
			'mp.stock > 0' => '', // Stock mayor a 0
			'm.id_canal' => $canal // Relación con el id_canal en marketplaces
		]);

		$stockCritico = 3;

		// Productos no publicados con stock >= 3
		$data['sin_publicar'] = $this->contar_productos([
			'mp.activo' => 0, // No activo en marketplaces_productos
			'p.disponibles >= ' . $stockCritico => '', // Disponibles en cantidad >= 3
			'm.id_canal' => $canal // Relación con el id_canal en marketplaces
		]);

		// Productos publicados sin stock cargado en el marketplace
		$data['sin_recargar'] = $this->contar_productos([
			'mp.activo' => 1, // Activo en marketplaces_productos
			'mp.stock' => 0, // Sin stock
			'p.disponibles >= ' . $stockCritico => '', // Disponibles en cantidad >= 3
			'm.id_canal' => $canal // Relación con el id_canal en marketplaces
		]);

		// Productos publicados con stock disponible < cargado en el marketplace
		$data['sin_remover'] = $this->contar_productos([
			'mp.activo' => 1, // Activo en marketplaces_productos
			'mp.stock > 0' => '', // Stock mayor a 0
			'p.disponibles < ' . $stockCritico => '', // Disponibles en cantidad < 3
			'm.id_canal' => $canal // Relación con el id_canal en marketplaces
		]);

		$data['publicados_semanas'] = $this->productos_publicados_por_semana($canal, $data['semanas']);

		$disponiblesCat = $this->disponibles_por_categoria($canal);
		// Setear el array de salida
		foreach ($disponiblesCat as $cd) {
			$data['disponibles_categorias'][$cd['nombre']] = $cd['disponibles_categoria'];
			$data['colores_categorias'][$cd['nombre']] = $cd['color'];
		}

		

		return $data;
	}

	private function contar_ventas($canal, $inicio, $fin) {
		return $this->db->select('COUNT(id) as ventas, SUM(total) as facturado')
						->from('ventas')
						->where('id_canal', $canal)
						->where('fecha_venta >=', $inicio . ' 00:00:00')
						->where('fecha_venta <=', $fin . ' 23:59:59')
						->where('deleted', 0)
						->get()->row_array();
	}
	
	private function get_ids_ventas($canal, $inicio, $fin) {
		$ventas = $this->db->select('id')
						   ->from('ventas')
						   ->where('id_canal', $canal)
						   ->where('fecha_venta >=', $inicio . ' 00:00:00')
						   ->where('fecha_venta <=', $fin . ' 23:59:59')
						   ->where('deleted', 0)
						   ->get()->result_array();
		return array_column($ventas, 'id');
	}
	
	private function top_productos($idsVentas, $noFisicos) {
		$result = [];
		$query = $this->db->select('p.nombre, SUM(precio_total) as suma_total')
						  ->from('productos_ventas pv')
						  ->join('productos p', 'pv.id_producto = p.id')
						  ->where_in('pv.id_venta', $idsVentas)
						  ->where_not_in('p.id_categoria', $noFisicos)
						  ->where(['p.deleted' => 0, 'pv.deleted' => 0])
						  ->group_by('p.id, p.nombre')
						  ->order_by('suma_total', 'DESC')
						  ->limit(10)
						  ->get()->result_array();
		foreach ($query as $row) {
			$result[$row['nombre']] = $row['suma_total'];
		}
		return $result;
	}
	
	private function top_categorias($idsVentas, $noFisicos) {
		$result = [];
		$query = $this->db->select('c.nombre, SUM(precio_total) as suma_total')
						  ->from('productos_ventas pv')
						  ->join('productos p', 'pv.id_producto = p.id')
						  ->join('categorias c', 'p.id_categoria = c.id')
						  ->where_in('pv.id_venta', $idsVentas)
						  ->where_not_in('p.id_categoria', $noFisicos)
						  ->where(['p.deleted' => 0, 'pv.deleted' => 0, 'c.deleted' => 0])
						  ->group_by('c.id, c.nombre')
						  ->order_by('suma_total', 'DESC')
						  ->limit(10)
						  ->get()->result_array();
		foreach ($query as $row) {
			$result[$row['nombre']] = $row['suma_total'];
		}
		return $result;
	}
	
	private function ventas_por_semana($canal) {
		$data = [];
		$hoy = date("Y-m-d");
		$inicio = (date("D") == "Mon") ? $hoy : date("Y-m-d", strtotime('last Monday'));
		$firstMonday = date('Y-m-d', strtotime('-7 weeks', strtotime($inicio)));
	
		// Inicializa semanas vacías
		for ($i = 0; $i < 8; $i++) {
			$alias = date('d-m', strtotime("+" . $i . " week", strtotime($firstMonday)));
			$data[$alias] = 0;
		}
	
		$query = $this->db->select('DATE_FORMAT(DATE_ADD(fecha_venta, INTERVAL(-WEEKDAY(fecha_venta)) DAY), "%d-%m") as semana, SUM(total) as suma_total')
						  ->from('ventas')
						  ->where('id_canal', $canal)
						  ->where('fecha_venta >=', $firstMonday)
						  ->where('deleted', 0)
						  ->group_by('semana')
						  ->order_by('semana')
						  ->get()->result_array();
	
		foreach ($query as $row) {
			$data[$row['semana']] = $row['suma_total'];
		}
	
		return $data;
	}
	
	private function contar_productos($filtros) {

		$this->db->select('COUNT(p.id) as cantidad')
				 ->from('productos p')
				 ->join('marketplaces_productos mp', 'mp.id_producto = p.id', 'left') // Relacionamos productos con marketplaces_productos
				 ->join('marketplaces m', 'm.id = mp.id_marketplace', 'left') // Relacionamos marketplaces
				 ->where('p.deleted', 0);
	
		foreach ($filtros as $campo => $valor) {
			if (strpos($campo, ' ') !== false) {
				$this->db->where($campo, NULL, false); // evita escape automático
			} else {
				$this->db->where($campo, $valor);
			}
		}
	
		return $this->db->get()->row_array()['cantidad'];
	}

	private function productos_publicados_por_semana($idCanal, $semanasBase) {
		$keys = array_keys($semanasBase);

		$currentYear  = (int)date('Y');
		$currentMonth = (int)date('n');

		[$d, $m] = array_map('intval', explode('-', $keys[0]));
		$year = ($m > $currentMonth) ? $currentYear - 1 : $currentYear;

		$weekDates = [];
		foreach ($keys as $key) {
			[$day, $month] = array_map('intval', explode('-', $key));

			if ($month === 1 && $m === 12) {
				$year++;
			}

			$weekDates[$key] = sprintf('%04d-%02d-%02d', $year, $month, $day);
			$m = $month;
		}

		$firstMonday = min($weekDates);

		$query = $this->db
			->select('
				DATE_FORMAT(
					DATE_ADD(mp.fecha_creacion, INTERVAL(-WEEKDAY(mp.fecha_creacion)) DAY),
					"%d-%m"
				) as semana,
				COUNT(p.id) as publicados
			')
			->from('productos p')
			->join('marketplaces_productos mp', 'mp.id_producto = p.id', 'left')
			->join('marketplaces m', 'm.id = mp.id_marketplace', 'left')
			->where('m.id_canal', $idCanal)
			->where('p.deleted', 0)
			->where('mp.fecha_creacion >=', $firstMonday)
			->group_by('semana')
			->order_by('mp.fecha_creacion')
			->get()
			->result_array();

		$result = array_fill_keys($keys, 0);

		foreach ($query as $row) {
			if (isset($result[$row['semana']])) {
				$result[$row['semana']] = (int)$row['publicados'];
			}
		}

		return $result;
	}

	
	private function disponibles_por_categoria($idCanal) {
		return $this->db->select('c.nombre, c.color, COUNT(p.id) as disponibles_categoria')
						->from('productos p')
						->join('categorias c', 'p.id_categoria = c.id')
						->join('marketplaces_productos mp', 'mp.id_producto = p.id', 'left') // Relacionamos productos con marketplaces_productos
						->join('marketplaces m', 'm.id = mp.id_marketplace', 'left') // Relacionamos marketplaces con el canal usando el id_canal
						->where('m.id_canal', $idCanal) // Aseguramos que el producto esté asociado con el canal adecuado
						->where('mp.activo', 1) // El producto debe estar activo en ese canal
						->where('mp.stock >', 0) // El producto debe tener stock mayor a 0
						->where('p.deleted', 0)
						->group_by('c.id')
						->get()->result_array();
	}

	public function marketplace($idCanal, $filtros) {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('
			p.id, 
			p.ps_id, 
			sa.id_product, 
			p.nombre as nombre, 
			p.stock, 
			sa.physical_quantity as ps_stock, 
			p.disponibles, 
			sa.quantity as ps_disponibles, 
			mp.stock as stock_marketplace, 
			mp.activo as activo_marketplace, 
			mp.id_marketplace, 
			m.id_canal, 
			p.id_ranking, 
			pl.available_now, 
			pl.available_later
		');
		$this->db->from('productos p');
		$this->db->join($dbps . '.ps_stock_available sa', 'p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps . '.ps_product_lang pl', 'p.ps_id = pl.id_product', 'left');

		// Join a marketplaces_productos (con deleted = 0)
		$this->db->join('marketplaces_productos mp', 'mp.id_producto = p.id AND mp.deleted = 0', 'left');

		// Aquí filtramos sólo los marketplaces del canal correspondiente (de forma INNER para evitar duplicados y nulos)
		$this->db->join('marketplaces m', 'm.id = mp.id_marketplace AND m.deleted = 0', 'inner');
		$this->db->where('m.id_canal', $idCanal);

		// Filtros base
		$this->db->where('p.deleted', 0);
		// Ignora insumos, demos y virtuales
		$this->db->where_not_in('p.id_categoria', [7, 10, 11]);

		if (isset($filtros['estado'])) {
			$this->db->where('mp.activo', $filtros['estado']);
		}

		if (!empty($filtros['stock'])) {
			if ($filtros['stock'] == 1) {
				$this->db->where('p.disponibles >', 0);
			} else {
				$this->db->where('p.disponibles <=', 0);
			}
		}

		if (!empty($filtros['categoria'])) {
			$this->db->where('p.id_categoria', $filtros['categoria']);
		}

		if (!empty($filtros['proveedor'])) {
			$this->db->where('p.id_proveedor', $filtros['proveedor']);
		}

		$this->db->order_by('p.disponibles', 'DESC');

		$productos = $this->db->get()->result_array();
		//prx($productos);
		$productos = $this->agregarMarketplaces($productos);

		return $productos;
	}
	

	public function mp_problemas($idCanal) {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		// Productos publicados con stock disponible < cargado en ML
		$this->db->select('
			p.id, 
			p.ps_id, 
			sa.id_product, 
			p.nombre, 
			p.stock, 
			p.id_ranking, 
			SUM(ap.disponibles) AS disponibles_online, 
			sa.physical_quantity AS ps_stock, 
			p.disponibles, 
			sa.quantity AS ps_disponibles, 
			mp.stock AS stock_marketplace, 
			p.reservados, 
			sa.reserved_quantity AS ps_reservados
		');
		$this->db->from('productos p');
		$this->db->join('marketplaces_productos mp', 'p.id = mp.id_producto', 'left'); // Relacionamos productos con marketplaces_productos
		$this->db->join('marketplaces m', 'm.id = mp.id_marketplace', 'left'); // Relacionamos marketplaces_productos con marketplaces
		$this->db->join('almacenes_productos ap', 'p.id = ap.id_producto', 'left'); // Relacionamos productos con almacenes_productos
		$this->db->join('almacenes a', 'a.id = ap.id_almacen', 'left'); // Relacionamos almacenes_productos con almacenes
		$this->db->join($dbps . '.ps_stock_available sa', 'p.ps_id = sa.id_product', 'left'); // Relacionamos productos con ps_stock_available (Base de datos externa)
		$this->db->where('m.id_canal', $idCanal); // Filtramos por el id del canal (marketplace) de la nueva estructura
		$this->db->where('mp.activo', 1); // Aseguramos que el producto esté activo en el marketplace
		$this->db->where('mp.stock >', 0); // Aseguramos que el producto tenga stock en el marketplace
		$this->db->where('p.deleted', 0); // Aseguramos que el producto no esté eliminado
		$this->db->where('mp.deleted', 0); // Aseguramos que el registro en marketplaces_productos no esté eliminado
		$this->db->where('a.deleted', 0); // Aseguramos que el almacén no esté eliminado
		$this->db->where('a.stock_online', 1); // Aseguramos que el almacén esté marcado como "stock online"
		$this->db->where('p.ignora_descuadre', 0); // Aseguramos que el producto no esté marcado para ignorar descuadres
		$this->db->group_by('
			p.id, 
			p.ps_id, 
			sa.id_product, 
			p.nombre, 
			p.stock, 
			p.id_ranking, 
			sa.physical_quantity, 
			p.disponibles, 
			sa.quantity, 
			mp.stock, 
			p.reservados, 
			sa.reserved_quantity
		'); // Agrupamos por todas las columnas necesarias para el resultado
		$this->db->having('SUM(ap.disponibles) < 3'); // Filtramos los productos cuyo stock disponible sea menor a 3
		$this->db->order_by('SUM(ap.disponibles) DESC'); // Ordenamos los resultados por stock disponible descendente
		$data['sin_remover'] = $this->db->get()->result_array(); // Ejecutamos la consulta y almacenamos los resultados
		$data['sin_remover'] = $this->agregarMarketplaces($data['sin_remover']);


		// Productos sin stock cargado en el marketplace pero con stock disponible en almacenes online
		$this->db->select('
			p.id, 
			p.ps_id, 
			sa.id_product, 
			p.nombre, 
			p.stock, 
			p.id_ranking, 
			SUM(ap.disponibles) AS disponibles_online, 
			sa.physical_quantity AS ps_stock, 
			p.disponibles, 
			sa.quantity AS ps_disponibles, 
			mp.stock AS stock_marketplace, 
			p.reservados, 
			sa.reserved_quantity AS ps_reservados
		');
		$this->db->from('productos p');
		$this->db->join('almacenes_productos ap', 'p.id = ap.id_producto', 'left');
		$this->db->join('almacenes a', 'a.id = ap.id_almacen', 'left');
		$this->db->join('marketplaces_productos mp', 'p.id = mp.id_producto', 'left');
		$this->db->join('marketplaces m', 'mp.id_marketplace = m.id', 'left');
		$this->db->join($dbps . '.ps_stock_available sa', 'p.ps_id = sa.id_product', 'left');

		// Filtros base
		$this->db->where('p.deleted', 0);
		$this->db->where('ap.deleted', 0);
		$this->db->where('a.deleted', 0);
		$this->db->where('a.stock_online', 1);
		$this->db->where('p.ignora_descuadre', 0);

		// Filtros por canal
		$this->db->where('m.id_canal', $idCanal);
		$this->db->where('mp.activo', 1);
		$this->db->where('mp.stock', 0); // stock cero en el marketplace

		$this->db->group_by('
			p.id, 
			p.ps_id, 
			sa.id_product, 
			p.nombre, 
			p.stock, 
			p.id_ranking, 
			sa.physical_quantity, 
			p.disponibles, 
			sa.quantity, 
			mp.stock, 
			p.reservados, 
			sa.reserved_quantity
		');
		$this->db->having('SUM(ap.disponibles) >= 3');
		$this->db->order_by('SUM(ap.disponibles) DESC');

		$data['sin_recargar'] = $this->db->get()->result_array();
		$data['sin_recargar'] = $this->agregarMarketplaces($data['sin_recargar']);

		return $data;
	}


	/** 
	 * Entrega productos ordenados por ranking y por fecha de ranking modificada más antigua
	 */
	public function productos_rankings(){

		$campos = 'CASE WHEN ultima_compra > ultima_venta THEN TIMESTAMPDIFF(DAY, ultima_compra, NOW()) WHEN ultima_compra <= ultima_venta THEN TIMESTAMPDIFF(DAY, ultima_venta, NOW()) END AS sin_venta, TIMESTAMPDIFF(DAY, fecha_ranking, NOW()) AS dias_ranking';

		$this->db->select('*, ' . $campos);
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->order_by('id_ranking', 'ASC');
		$this->db->order_by('id_categoria', 'ASC');
		$this->db->order_by('sin_venta', 'DESC');
		$this->db->order_by('fecha_ranking', 'ASC');
		$this->db->order_by('ultima_venta', 'ASC');
		$this->db->order_by('ultima_compra', 'ASC');
		$productos = $this->db->get()->result_array();

		foreach ($productos as $p) {
			if ($p['disponibles'] > 0) {
				$tmp_productos['disponibles'][$p['id_ranking']][$p['id_categoria']][] = $p;
			}
			else {
				$tmp_productos['agotados'][$p['id_ranking']][$p['id_categoria']][] = $p;
			}
		}

		return $tmp_productos;
	}

	/**
	 * Cuenta o retorna descuadres que hay que corregir de forma urgente
	 */
	public function diffs($detalle) {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		// Busca Productos que no sean preventas que estén publicados en PS > a Disponibles Online
		$this->db->select('p.id, sa.id_product, p.nombre, p.stock, sum(CASE WHEN a.stock_online = 1 THEN ap.disponibles ELSE (CASE WHEN ap.disponibles < 0 THEN ap.disponibles ELSE 0 END) END) as disponibles_online, sa.physical_quantity as ps_stock, p.disponibles, sa.quantity as ps_disponibles, p.reservados, sa.reserved_quantity as ps_reservados, pl.available_now, pl.available_later');
		$this->db->from('productos p');
		$this->db->join('almacenes_productos ap','ON p.id = ap.id_producto', 'left');
		$this->db->join('almacenes a','ON ap.id_almacen = a.id', 'left');
		$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps.'.ps_product_lang pl','ON p.ps_id = pl.id_product', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->where('ap.deleted', 0);
		$this->db->where('a.deleted', 0);
		$this->db->where_not_in('pl.available_now', ['Preventa', 'Próximamente', 'A Pedido']);
		$this->db->where_not_in('pl.available_later', ['Preventa', 'Próximamente', 'A Pedido']);
		$this->db->where('p.ignora_descuadre', 0);
		$this->db->group_by('p.id, sa.id_product, p.nombre, p.stock, sa.physical_quantity, p.disponibles, sa.quantity, p.reservados, sa.reserved_quantity, pl.available_now, pl.available_later');
		$this->db->having('sum(CASE WHEN a.stock_online = 1 THEN ap.disponibles ELSE (CASE WHEN ap.disponibles < 0 THEN ap.disponibles ELSE 0 END) END) < `sa`.`quantity`');
		$this->db->order_by('p.nombre ASC');
		$diferencias_ps = $this->db->get()->result_array();
		$diferencias_ps = $this->agregarMarketplaces($diferencias_ps);

		// Consultamos los productos con su stock en almacenes y stock de marketplaces
		$this->db->select('p.id, p.nombre, p.stock, sa.physical_quantity as ps_stock, 
			p.disponibles, sa.quantity as ps_disponibles, p.reservados, sa.reserved_quantity as ps_reservados,
			mp.id_marketplace, mp.stock as stock_marketplace, sum(CASE WHEN a.stock_online = 1 THEN ap.disponibles ELSE (CASE WHEN ap.disponibles < 0 THEN ap.disponibles ELSE 0 END) END) as disponibles_online');
		$this->db->from('productos p');
		$this->db->join('almacenes_productos ap', 'ON p.id = ap.id_producto', 'left');
		$this->db->join('almacenes a', 'ON ap.id_almacen = a.id', 'left');
		$this->db->join($dbps . '.ps_stock_available sa', 'ON p.ps_id = sa.id_product', 'left');
		$this->db->join('marketplaces_productos mp', 'ON p.id = mp.id_producto', 'left');
		$this->db->join('marketplaces m', 'ON m.id = mp.id_marketplace', 'left');
		
		// Filtramos solo los productos activos y aquellos con stock en el almacén online
		$this->db->where('p.deleted', 0);
		$this->db->where('ap.deleted', 0);
		$this->db->where('a.deleted', 0);
		$this->db->where('a.stock_online', 1); // Solo productos online
		$this->db->where('mp.stock > 0'); // Solo productos con stock cargado en MP
		$this->db->where('p.ignora_descuadre', 0);
		$this->db->where('mp.deleted', 0); // Marketplace no eliminado
		$this->db->where('m.deleted', 0);  // Marketplace no eliminado
		$this->db->where('m.activo', 1);   // Marketplace activo

		// Realizamos el grupo por productos para obtener una fila por producto y marketplace
		$this->db->group_by('p.id, p.nombre, p.stock, sa.physical_quantity, p.disponibles, sa.quantity, p.reservados, sa.reserved_quantity, mp.id_marketplace');

		// Solo nos interesan los productos donde hay diferencia de stock
		$this->db->having('sum(CASE WHEN a.stock_online = 1 THEN ap.disponibles ELSE (CASE WHEN ap.disponibles < 0 THEN ap.disponibles ELSE 0 END) END) < mp.stock');
		
		// Ejecutamos la consulta
		$productosConDiferencias = $this->db->get()->result_array();

		// Ahora procesamos las diferencias, calculando la diferencia real entre el stock
		$diferencias_mp = [];
		foreach ($productosConDiferencias as $producto) {
			// Aquí calculamos la diferencia de stock entre el almacén y los marketplaces
			$diferencia = $producto['stock_marketplace'] - $producto['disponibles_online'];
			if ($diferencia > 0) {
				$producto['diferencia'] = $diferencia;
				$diferencias_mp[] = $producto;
			}
		}

		$diferencias_mp = $this->agregarMarketplaces($diferencias_mp);

		$diferencias = array_merge($diferencias_ps, $diferencias_mp);

		$_SESSION['diffs'] = count($diferencias);
		if ($detalle) {
			return $diferencias;
		}
	}

	public function codigos_repetidos() {
		$result = [];
		$this->db->select('codigo_barra, count(id)');
		$this->db->from('productos');
		$this->db->where('codigo_barra !=', '');
		$this->db->where('codigo_barra !=', 0);
		$this->db->where('deleted', 0);
		$this->db->group_by('codigo_barra');
		$this->db->having('count(id) > 1');

		$repetidos = $this->db->get()->result_array();
		$_SESSION['codigos_repetidos'] = count($repetidos);
		if (!empty($repetidos)) {
			//$codigos = [];
			foreach ($repetidos as $c) {
				$codigos[] = $c['codigo_barra'];
			}

			$this->db->select('*');
			$this->db->from('productos');
			$this->db->where_in('codigo_barra ', $codigos);
			$this->db->where('deleted', 0);
			$this->db->order_by('codigo_barra', 'ASC');

			$productos = $this->db->get()->result_array();

			foreach ($productos as $p) {
				$result[$p['codigo_barra']][] = $p;
			}
		}
		return $result;
	}

	/**
	 * Agrega los el campo marketplaces con toda la info existente de marketplaces
	 */
	public function agregarMarketplaces($productos)	{
		if (empty($productos)) {
			return [];
		}

		// Obtener los IDs de los productos
		$productoIds = array_column($productos, 'id');

		// Obtener los registros de marketplaces_productos para esos productos
		$marketplacesData = $this->db->select('id_producto, id_marketplace, activo, stock, fecha_creacion')
			->from('marketplaces_productos')
			->where_in('id_producto', $productoIds)
			->get()
			->result_array();

		// Reorganizar los datos por producto
		$marketplacesPorProducto = [];
		foreach ($marketplacesData as $mp) {
			$marketplacesPorProducto[$mp['id_producto']][] = [
				'idMarketplace' => (int) $mp['id_marketplace'],
				'activo' => (int) $mp['activo'],
				'stock' => (int) $mp['stock'],
				'fechaCreacion' => $mp['fecha_creacion']
			];
		}

		// Agregar los marketplaces al array de productos
		foreach ($productos as &$producto) {
			if (!empty($marketplacesPorProducto[$producto['id']])) {
				foreach($marketplacesPorProducto[$producto['id']] as $mp) {
					$producto['marketplaces'][$mp['idMarketplace']] = $mp;
				}
			}
			else {
				$producto['marketplaces'] = [];
			}
		}
		return $productos;
	}


	/**
	 * Entrega la cantidad de productos disponibles en cada almacen según el $id recibido
	 * Si no recibe id entrega todos los almacenes_productos por id de producto
	 * si $revisar se recibe en true se contarán los reservados de las ventas pendientes
	 */
	public function stock_almacenes($id = null, $revisar = false) {
		$this->db->select('a.id as id_almacen, a.nombre, a.activo, ap.id as id_almacen_producto, ap.id_producto, ap.stock, ap.disponibles, ap.reservados, ap.en_transito, ap.deseados, ap.deseados_max, ap.id_ubicacion, ap.error');
		$this->db->from('almacenes a');
		
		if ($id) {
			$this->db->join('almacenes_productos ap', 'ap.id_almacen = a.id AND ap.deleted = 0 AND ap.id_producto = ' . $id, 'left');
		}
		else {
			$this->db->join('almacenes_productos ap', 'ap.id_almacen = a.id AND ap.deleted = 0', 'left');
		}
		$this->db->where('a.deleted', 0);
		$stocks = $this->db->get()->result_array();

		$result = [];
		foreach ($stocks as $almacen) {
			if ($id) {
				$result[$almacen['id_almacen']] = $almacen;
			}
			else {
				$result[$almacen['id_producto']][$almacen['id_almacen']] = $almacen;
			}
		}

		if (empty($result) && $revisar) {
			$reservados = $this->reservados_almacenes($id);
			foreach ($reservados as $idAlmacen => $almacen) {
				$result[$idAlmacen]['reservados'] = $reservados[$idAlmacen] ?? 0;
				$result[$idAlmacen]['disponibles'] = ($almacen['stock'] ?? 0) - ($reservados[$idAlmacen] ?? 0);
			}
		}

		return $result;
	}

	/**
	 * Entrega la información de los market places para el producto solicitado
	 */
	public function marketplaces($id = null) {
		$this->db->select('mp.id_marketplace, mp.stock, mp.activo, mp.fecha_creacion, m.nombre as nombre_marketplace');
		$this->db->from('marketplaces_productos mp');
		$this->db->join('marketplaces m', 'm.id = mp.id_marketplace');
		$this->db->where('mp.id_producto', $id);
		$this->db->where('mp.deleted', 0);  // marketplace_producto no debe estar eliminado
		$this->db->where('mp.activo', 1);  // marketplace_producto debe estar activo
		$this->db->where('m.deleted', 0);   // marketplace no debe estar eliminado
		$this->db->where('m.activo', 1);    // marketplace debe estar activo
		$marketplaces = $this->db->get()->result_array();

		$producto['marketplaces'] = [];

		foreach ($marketplaces as $mp) {
			$producto['marketplaces'][$mp['id_marketplace']] = [
				'nombre' => $mp['nombre_marketplace'],
				'stock' => (int) $mp['stock'],
				'activo' => (bool) $mp['activo'],
				'fecha_creacion' => $mp['fecha_creacion']
			];
		}
		return $producto['marketplaces'];
	}

	/**
	 * Calcula si el stock de los almacenes coincide con el stock del producto
	 * Si recibe id retorna true o false
	 */
	public function valida_stock($id) {
		$almacenes = $this->stock_almacenes($id);
		$this->db->select('stock');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$this->db->where('deleted', 0);
		$producto = $this->db->get()->row_array();

		$totalAlmacenes = 0;
		foreach ($almacenes as $almacen) {
			$totalAlmacenes += $almacen['stock'];
		}

		return $producto['stock'] == $totalAlmacenes;
	}


	/**
	 * Entrega lista de codigos de barra de productos
	 */
	public function codigos($filtros = []){
		// Busca todos los productos según los filtros recibidos

		$this->db->select('p.id, p.ps_id, p.nombre, id_categoria, id_proveedor, codigo_barra, p.disponibles, p.id_ranking, sum(ap.disponibles) as disponibles_online');
		$this->db->from('productos p');
		/** */
		$this->db->join('almacenes_productos ap','ON p.id = ap.id_producto', 'left');
		$this->db->join('almacenes a','ON ap.id_almacen = a.id', 'left');
		//$this->db->where('p.deleted', 0);
		$this->db->where('ap.deleted', 0);
		$this->db->where('a.deleted', 0);
		$this->db->where('a.stock_online', 1);
		//$this->db->where('p.ignora_descuadre', 0);
		//No busca insumos, demos ni virtuales
		$this->db->where_not_in('id_categoria', [7, 10, 11]);
		$this->db->group_by('p.id');

		/** */
		$this->db->where('p.deleted', 0);

		// Filtra productos sin código
		if ($filtros['codigo'] == 1) {
			$this->db->where('codigo_barra !=', '');
		}
		elseif ($filtros['codigo'] == 0) {
			$this->db->where('codigo_barra', '');
		}

		if ($filtros['existencia'] == 1) {

			$this->db->having('sum(ap.disponibles) >', 0);

		}
		elseif ($filtros['existencia'] == 0) {
			$this->db->having('sum(ap.disponibles) <', 1);
		}

		if (!empty($filtros['categoria'])) {
			$this->db->where('id_categoria =', $filtros['categoria']);
		}

		if (!empty($filtros['proveedor'])) {
			$this->db->where('id_proveedor =', $filtros['proveedor']);
		}
		if (!empty($filtros['busqueda'])) {
			$keywords = explode(" ", $filtros['busqueda']);
			foreach ($keywords as $key) { 
					$this->db->like('p.nombre', $key);
			}
		}

		if ($filtros['cantidad'] > 0) {
			$this->db->limit($filtros['cantidad']);
		}

		$productos = $this->db->get()->result_array();
		//pr($this->db->last_query());
		
		return $productos;
	}

	/**
	 * Entrega datos para imprimir codigos de barra segun los ids recibidos
	 */
	public function datos_codigos($ids) {
		$this->db->select('id, nombre, codigo_barra');
		$this->db->from('productos');
		$this->db->where_in('id', $ids);
		$this->db->where('deleted', 0);
		$productos = $this->db->get()->result_array();

		foreach ($productos as &$p) {
			$codigo = substr(strval($p['codigo_barra']), 0, -1);
			$codigo = str_pad($codigo, 12, '0',  STR_PAD_LEFT);
			$p['codigo_barra'] = $codigo;
			$prs[$p['id']] = $p;
		}

		foreach($ids as $id) {
			$result[] = $prs[$id];
		}

		return $result;
	}

	public function datos_precios($ids) {
		$this->db->select('id, nombre, ps_id');
		$this->db->from('productos');
		$this->db->where_in('id', $ids);
		$this->db->where('deleted', 0);
		$productos = $this->db->get()->result_array();

		foreach ($productos as &$p) {
			$p['ps_price'] = $this->ps_prices($p['ps_id'], 1);
			$precios[$p['id']] = $p;
		}

		foreach($ids as $id) {
			$result[] = $precios[$id];
		}
		return $result;
	}

	/**
	 * Entrega prodcutos con cambio de precio según $ini, $fin
	 */
	public function log_cambio_precio($ini = '', $fin = ''){
		if (empty($ini)){
			$ini = date('Y-m-d', strtotime('-7 days', strtotime(date('Y-m-d'))));
		}

		$this->db->select('*, pl.created as fecha_cambio');
		$this->db->from('precios_log pl');
		$this->db->join('productos p','pl.id_producto = p.id', 'left');
		$this->db->where('pl.created >=', $ini);
		if (!empty($fin)) {
			$this->db->where('pl.created <=', $fin);
		}
		$this->db->where('p.deleted', 0);
		$this->db->order_by('pl.created', 'DESC');

		$productos = $this->db->get()->result_array();

		return $productos;
	}


	public function get_by_codigo($codigo) {
		return $this->db
			->select('id, nombre')
			->where('codigo_barra', $codigo)
			->get('productos')
			->row_array();
	}



	/**
	 * Retorna los datos necesarios para crear y editar productos: Proveedores (distribuidores), Categorías, Rankings
	 * 
	 */
	public function datos_editar($data) {
		$CI =& get_instance();
		
		// Obtiene los distribuidores
		$CI->load->model('proveedor_model', 'Proveedor');
		$data['distribuidores'] = $this->Proveedor->lista(true);

		// Obtiene las categorias de productos
		$CI->load->model('categoria_model', 'Categoria');
		$data['categorias'] = $this->Categoria->list();

		// Obtiene rankings existentes
		$data['rankings'] = $this->list_rankings();

		return $data;
	}


	public function agotados_online() {
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		// Subconsulta última venta por producto y canal
		$subquery = $this->db
			->select('pv.id_producto, v.id_canal, MAX(v.fecha_venta) AS ultima_venta')
			->from('productos_ventas pv')
			->join('ventas v', 'v.id = pv.id_venta', 'left')
			->where_in('v.id_canal', [1,7,8,12])
			->group_by(['pv.id_producto','v.id_canal'])
			->get_compiled_select();

		// Consulta principal
		$this->db->select("
			p.id,
			p.nombre,
			p.id_categoria,
			p.ps_id,
			p.id_ranking,
			pi.id_image AS ps_imagen,
			a.id AS id_almacen,
			ap.disponibles AS stock_disponible,
			lv.id_canal,
			lv.ultima_venta
		");
		$this->db->from('productos p');
		$this->db->join($dbps.'.ps_image pi', 'p.ps_id = pi.id_product AND pi.cover = 1', 'left');
		$this->db->join('almacenes_productos ap', 'p.id = ap.id_producto AND ap.deleted = 0', 'left');
		$this->db->join('almacenes a', 'a.id = ap.id_almacen AND a.activo = 1', 'left');
		$this->db->join("($subquery) lv", 'lv.id_producto = p.id', 'left');

		$this->db->where('p.deleted', 0);
		$this->db->where_not_in('p.id_categoria', [7, 10, 11]);

		// Sin stock en almacenes online activos
		$this->db->where("NOT EXISTS (
			SELECT 1
			FROM almacenes_productos ap1
			JOIN almacenes a1 ON a1.id = ap1.id_almacen
			WHERE ap1.id_producto = p.id
			AND a1.stock_online = 1
			AND a1.activo = 1
			AND ap1.disponibles > 0
		)", null, false);

		// Con stock en almacenes offline activos
		$this->db->where("EXISTS (
			SELECT 1
			FROM almacenes_productos ap2
			JOIN almacenes a2 ON a2.id = ap2.id_almacen
			WHERE ap2.id_producto = p.id
			AND a2.stock_online = 0
			AND a2.activo = 1
			AND ap2.disponibles > 0
		)", null, false);

		$this->db->order_by('p.nombre', 'ASC');

		$query = $this->db->get()->result_array();

		$result = ['agotados_online' => []];
		$ps_ids = [];

		// Armamos el array jerárquico por categoría y producto
		foreach ($query as $row) {
			$categoria = $row['id_categoria'];
			$id_producto = $row['id'];
			$ps_ids[$id_producto] = $row['ps_id'];

			if (!isset($result['agotados_online'][$categoria])) {
				$result['agotados_online'][$categoria] = [];
			}

			if (!isset($result['agotados_online'][$categoria][$id_producto])) {
				$result['agotados_online'][$categoria][$id_producto] = [
					'id' => $id_producto,
					'nombre' => $row['nombre'],
					'ps_id' => $row['ps_id'],
					'imagen' => $row['ps_imagen'],
					'id_ranking' => $row['id_ranking'],
					'disponibles' => [],
					'disponibles_general' => 0,
					'ultima_venta' => [
						1 => null,
						7 => null,
						8 => null,
						12 => null
					],
					'ps_alertas_count' => 0 // Inicializamos contador de alertas
				];
			}

			// Disponibles por almacén
			if ($row['id_almacen'] !== null) {
				$result['agotados_online'][$categoria][$id_producto]['disponibles'][$row['id_almacen']] = $row['stock_disponible'];
			}

			// Última venta por canal
			if ($row['id_canal'] !== null) {
				$current = $result['agotados_online'][$categoria][$id_producto]['ultima_venta'][$row['id_canal']];
				$fecha = $row['ultima_venta'];
				if ($current === null || strtotime($fecha) > strtotime($current)) {
					$result['agotados_online'][$categoria][$id_producto]['ultima_venta'][$row['id_canal']] = $fecha;
				}
			}
		}

		// Calculamos disponibles_general por producto
		foreach ($result['agotados_online'] as $categoria => &$productos) {
			foreach ($productos as &$producto) {
				$producto['disponibles_general'] = array_sum($producto['disponibles']);
			}
		}
		unset($productos);

		// --- Contamos alertas por producto ---
		if (!empty($ps_ids)) {
			$ps = $this->load->database('prestashop', TRUE);
			$ps->select('m.id_product, COUNT(*) AS alertas_count');
			$ps->from('ps_mailalert_customer_oos m');
			$ps->where_in('m.id_product', $ps_ids);
			$ps->group_by('m.id_product');

			$alertas_count = $ps->get()->result_array();

			// Convertimos a array asociativo por ps_id
			$alertas_por_producto = [];
			foreach ($alertas_count as $a) {
				$alertas_por_producto[$a['id_product']] = $a['alertas_count'];
			}

			// Asignamos el conteo de alertas a cada producto
			foreach ($result['agotados_online'] as &$productos) {
				foreach ($productos as &$producto) {
					$pid = $producto['ps_id'];
					$producto['alertas'] = isset($alertas_por_producto[$pid]) ? $alertas_por_producto[$pid] : 0;
				}
			}
			unset($productos);
		}

		return $result;
	}

	public function getVentasDetalladas($fecha_inicio = null, $fecha_fin = null, $id_canal = null) {
		$this->db->select("
			v.id AS id_venta,
			DATE(v.fecha_venta) AS fecha,
			TIME(v.fecha_venta) AS hora,
			d.nombre as tipo_documento,
			v.boleta as numero_boleta,
			v.id_cliente,
			c.nombre AS canal,
			
			pv.id_producto,
			p.nombre AS producto,
			cat.nombre AS categoria,
			pv.cantidad,
			pv.precio,
			pv.descuento,
			pv.precio_total
		");
		
		$this->db->from('ventas v');
		$this->db->join('productos_ventas pv', 'v.id = pv.id_venta');
		$this->db->join('productos p', 'pv.id_producto = p.id');
		$this->db->join('categorias cat', 'p.id_categoria = cat.id');
		$this->db->join('canales c', 'v.id_canal = c.id');
		$this->db->join('tipos_documentos d', 'v.id_tipo_documento = d.id');

		if (!empty($fecha_inicio)) {
			$this->db->where('DATE(v.fecha_venta) >=', $fecha_inicio);
		}
		if (!empty($fecha_fin)) {
			$this->db->where('DATE(v.fecha_venta) <=', $fecha_fin);
		}
		if (!empty($id_canal)) {
			$this->db->where('v.id_canal', $id_canal);
		}

		$this->db->order_by('v.fecha_venta', 'ASC');
		$this->db->order_by('v.id', 'ASC');

		return $this->db->get()->result();
	}


	/*******************************************************************************************/
	/*                              Funciones de Edición                                       */
	/*******************************************************************************************/

	public function save($data) {
		unset($data['ps_nombre']);

		// Validar Datos
		if (empty($data['nombre']))  {
			$errors[] = 'Debes indicar el nombre';
		}
		if (empty($data['id_categoria']))  {
			$errors[] = 'Debes seleccionar una categoria';
		}
		if (empty($data['id_proveedor']))  {
			$errors[] = 'Debes seleccionar un proveedor';
		}

		if (empty($errors)) {
			$error = false;
			$p = [];
			if (!empty($data['id'])) {
				// Busca info de producto
				$this->db->select('*');
				$this->db->from('productos');
				$this->db->where('id', $data['id']);
				$p = $this->db->get()->row_array();
			}

			$this->db->trans_begin();

			//revisar si viene id y si viene se hace update
			if (empty($data['id'])) {
				// Producto Nuevo

				$mpActivo = $data['mp_activo'];
				$mpStock = $data['mp_stock'];
				unset($data['mp_activo'], $data['mp_stock']);

				// Política reposición para producto nuevo
				if (isset($data['tipo_reposicion']) && $data['tipo_reposicion'] != 0) {
					$data['id_usuario_tipo_reposicion'] = $_SESSION['usuario']['id'];
					$data['fecha_tipo_reposicion'] = date('Y-m-d H:i:s');
				}

				// Si no es -1 limpiamos razones igual
				if ($data['tipo_reposicion'] != -1) {
					$data['razon_no_reponer'] = 0;
					$data['razon_no_reponer_detalle'] = null;
				}

				$this->db->insert('productos', $data);
				$return['id_producto'] = $this->db->insert_id();

				// Crear almacenes_productos inmediatamente
				$this->crear_almacenes_productos($return['id_producto']);

				// Setea Ranking
				$error = !$this->modifica_ranking($return['id_producto'], $data['id_ranking']);

				// Setea Marketplaces
				$error = !$this->save_marketplaces($return['id_producto'], $mpActivo, $mpStock);

			}
			else {
				// Edición de Producto
				if (empty($data['ps_id_asociado'])) {
					$data['ps_id_asociado'] = 0;
				}

				$mpActivo = $data['mp_activo'];
				$mpStock = $data['mp_stock'];
				unset($data['mp_activo'], $data['mp_stock']);

				// ================== POLÍTICA DE REPOSICIÓN ==================
				if (!empty($p)) { // solo si estamos editando
					$cambioPolitica = false;

					// 1) cambio de tipo de reposición
					if ($data['tipo_reposicion'] != $p['tipo_reposicion']) {
						$cambioPolitica = true;
					}

					// 2) si está en NO REPONER (-1) revisar cambios en razones
					if ($data['tipo_reposicion'] == -1) {

						$razonAntes = isset($p['razon_no_reponer']) ? (int)$p['razon_no_reponer'] : null;
						$razonAhora = isset($data['razon_no_reponer']) ? (int)$data['razon_no_reponer'] : null;

						$detalleAntes = isset($p['razon_no_reponer_detalle']) ? trim($p['razon_no_reponer_detalle']) : '';
						$detalleAhora = isset($data['razon_no_reponer_detalle']) ? trim($data['razon_no_reponer_detalle']) : '';

						if ($razonAntes !== $razonAhora || $detalleAntes !== $detalleAhora) {
							$cambioPolitica = true;
						}
					}

					if ($cambioPolitica) {
						$data['id_usuario_tipo_reposicion'] = $_SESSION['usuario']['id'];
						$data['fecha_tipo_reposicion'] = date('Y-m-d H:i:s');
					}

					// Si dejó de ser NO REPONER limpiamos razones
					if ($data['tipo_reposicion'] != -1) {
						$data['razon_no_reponer'] = 0;
						$data['razon_no_reponer_detalle'] = null;
					}
				}

				$this->db->where('id', $data['id']);
				$this->db->update('productos', $data);
				$return['id_producto'] = $data['id'];

				// Setear cambio de ranking
				if ($data['id_ranking'] != $p['id_ranking']) {
					$error = !$this->modifica_ranking($return['id_producto'], $data['id_ranking']);
				}

				// Setea Marketplaces
				$error = !$this->save_marketplaces($return['id_producto'], $mpActivo, $mpStock);

				// log de cambio solo para edición de productos
				// Buscar diferencias y guardar en log de productos
				$difs = [];
				foreach($data as $k => $v) {
					if (empty($p[$k]) && isset($v)) {
						if ($p[$k] != $v) {
							$difs[$k] = [
								'anterior' => '',
								'actual' => $v
							];
						}
					}
					elseif ($p[$k] != $v) {
						$difs[$k] = [
							'anterior' => $p[$k],
							'actual' => $v
						];
					}
				}

				if (!empty($difs)) {
					$log = [
						'tipo_entidad' => 1,
						'id_entidad' => $return['id_producto'],
						'id_usuario' => $_SESSION['usuario']['id'],
						'data' => json_encode($difs)
					];
					$this->db->insert('cambios_log', $log);
				}
			}

			if (!$error) {
				$this->db->trans_commit();
				$return['status'] = 'success';
				return $return;
			}
			else {
				$this->db->trans_rollback();
				$return['status'] = 'error';
				$return['message'] = 'Error al guardar los datos';
				return $return;
			}
		}
		else {
			// Retornar error
			$return['status'] = 'error';
			$return['message'] = implode('<br />', $errors);
			return $return;
		}
	}


	public function save_stock($data) {
		$stockAlmacen = [];
		if (isset($data['stock_almacen'])){
			$stockAlmacen = $data['stock_almacen'];
			unset($data['stock_almacen']);
		}

		$deseadosAlmacen = $data['deseados_almacen'];
		$deseadosMaxAlmacen = $data['deseados_max_almacen'];
		unset($data['deseados_almacen']);

		$ubicacionAlmacen = $data['id_ubicacion'];
		unset($data['id_ubicacion']);

		$error = false;
		$this->db->trans_begin();

		$return['id_producto'] = $data['id'];
		// Setear stock en almacenes

		$stocks = $this->actualiza_stocks($return['id_producto'], $stockAlmacen, $deseadosAlmacen, $deseadosMaxAlmacen, $ubicacionAlmacen);
		if ($stocks['status'] == 'error') {
			$error = $stocks['error'];
		}

		// Buscar diferencias y guardar en log de productos
		$difs = [];
		if (!empty($stocks['almacenes'])) {
			$difs['almacenes'] = $stocks['almacenes'];
		}

		if (!empty($difs)) {
			$log = [
				'tipo_entidad' => 1,
				'id_entidad' => $return['id_producto'],
				'id_usuario' => $_SESSION['usuario']['id'],
				'data' => json_encode($difs)
			];
			$this->db->insert('cambios_log', $log);
		}

		if (!$error) {
			$this->db->trans_commit();
			$return['status'] = 'success';
			return $return;
		}
		else {
			$this->db->trans_rollback();
			$return['status'] = 'error';
			$return['message'] = 'Error al guardar los datos';
			return $return;
		}

	}

	// Actualiza Ranking de producto en VDM y PS
	public function modifica_ranking($id, $id_ranking) {
		// Busca info de producto
		$this->db->select('ps_id, id_ranking');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$p = $this->db->get()->row_array();

		if (!empty($p)) {
			// Si hay cambio actualiza registro en Ventas
			$pn['id_ranking'] = $id_ranking;
			$pn['fecha_ranking'] = date('Y-m-d H:i:s');
			$this->db->where('id', $id);
			$this->db->update('productos', $pn);

			// Guarda Log
			$rl = [
				'id_producto' => $id,
				'id_ranking' => $id_ranking,
			];

			if (!empty($_SESSION['usuario']['id'])) {
				$rl['id_usuario'] = $_SESSION['usuario']['id'];
			}

			$this->db->insert('rankings_log', $rl);

			// Busca Info de otros Rankings
			$this->db->select('ps_id, ps_rule');
			$this->db->from('rankings');
			// Ranking Anterior
			$rks = $this->db->get()->result_array();
			foreach($rks as $rk){
				$ps_rankings[] = $rk['ps_id'];
				$ps_rules[] = $rk['ps_rule'];
			}

			// Elimina otra categoria de ranking en PS
			$ps = $this->load->database('prestashop', TRUE);

			$ps->where('id_product', $p['ps_id']);
			$ps->where_in('id_category', $ps_rankings);
			$ps->delete('ps_category_product');

			// Elimina descuento asociado a otro ranking (ps_specific_price)
			$ps->where('id_product', $p['ps_id']);
			//$ps->where('id_specific_price_rule', $ra['ps_rule']);
			$ps->where_in('id_specific_price_rule', $ps_rules);
			$ps->delete('ps_specific_price');

			// Busca Info de Ranking Nuevo
			$this->db->select('ps_id, ps_rule');
			$this->db->from('rankings');
			$this->db->where('id', $id_ranking);
			// Ranking Anterior
			$rn = $this->db->get()->row_array();

			if (!empty($rn)) {
				// Consulta position max en PS
				$ps->select('max(position) as pos');
				$ps->from('ps_category_product');
				$ps->where('id_category', $rn['ps_id']);
				// Ranking Anterior
				$pos = $ps->get()->row_array();

				// Crea Relación con categoría en PS
				$cp = [
					'id_category' => $rn['ps_id'],
					'id_product' => $p['ps_id'],
					'position' => $pos['pos'] + 1
				];	
				$ps->insert('ps_category_product', $cp);

				if (!empty($rn['ps_rule'])) {
					// Consulta Specific Price Rule
					$ps->select('reduction, reduction_type');
					$ps->from('ps_specific_price_rule');
					$ps->where('id_specific_price_rule', $rn['ps_rule']);
					// Ranking Anterior
					$spr = $ps->get()->row_array();
					$reduction = ($spr['reduction_type'] ? $spr['reduction']/100 : $spr['reduction']);

					// crea ps_specific_price
					$sp = [
						'id_specific_price_rule' => $rn['ps_rule'],
						'id_product' => $p['ps_id'],
						'id_shop' => 1,
						'price' => '-1.000000',
						'from_quantity' => 1,
						'reduction' => $reduction,
						'reduction_type' => $spr['reduction_type']
					];	
					$ps->insert('ps_specific_price', $sp);
				}
			}

			$this->actualiza_oferta_ps($p['ps_id']);
		}

		return true;
	}

	/*
	 * Actualización automática de rankings a productos según sus ventas
	 * Solo aplica para juegos que cuesten menos de 70.000
	 */
	public function actualiza_rankings($confirmacion = 0) {

		// Actualiza fechas de ultima compra y ultima venta
		$this->update_dates();

		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$cambios = [];

		// =========================
		// ALMACENES ONLINE
		// =========================

		$this->db->select('id');
		$this->db->from('almacenes');
		$this->db->where('stock_online', 1);
		$this->db->where('deleted', 0);

		$almacenesOnline = array_column(
			$this->db->get()->result_array(),
			'id'
		);

		/**
		 * Obtiene stock online
		 */
		$getStockOnline = function($idProducto) use ($almacenesOnline) {

			if (empty($almacenesOnline)) {
				return 0;
			}

			$this->db->select_sum('disponibles');

			$this->db->from('almacenes_productos');

			$this->db->where('id_producto', $idProducto);

			$this->db->where_in('id_almacen', $almacenesOnline);

			$this->db->where('deleted', 0);

			$row = $this->db->get()->row_array();

			return (int)($row['disponibles'] ?? 0);
		};

		// =========================================================
		// C -> D
		// =========================================================

		pr('Se actualizará el ranking a productos C sin ventas últimos 90 días');

		$this->db->select('
			p.id,
			p.ps_id,
			p.nombre,
			p.id_ranking,
			p.ultima_venta,
			p.ultima_compra,
			ps.price
		');

		$this->db->from('productos p');

		$this->db->join(
			$dbps . '.ps_product ps',
			'ps.id_product = p.ps_id'
		);

		$this->db->where('p.deleted', 0);

		$this->db->where('p.id_ranking', 4);

		$this->db->where('p.id_categoria', 1);

		$this->db->where('p.fecha_ranking < CURDATE() - INTERVAL 90 DAY');

		$this->db->group_start();
		$this->db->where('p.ultima_venta < CURDATE() - INTERVAL 90 DAY');
		$this->db->or_where('p.ultima_venta IS NULL', null, false);
		$this->db->group_end();

		$this->db->group_start();
		$this->db->where('p.ultima_compra < CURDATE() - INTERVAL 90 DAY');
		$this->db->or_where('p.ultima_compra IS NULL', null, false);
		$this->db->group_end();

		$this->db->where('ps.price <', 70000);

		$productos = $this->db->get()->result_array();

		foreach ($productos as $p) {

			$stockOnline = $getStockOnline($p['id']);

			if ($stockOnline <= 0) {
				continue;
			}

			if ($confirmacion) {
				$this->modifica_ranking($p['id'], 5);
				$this->actualiza_oferta_ps($p['ps_id']);
			}

			$p['stock_online'] = $stockOnline;

			$cambios['C->D'][] = $p;
		}

		// =========================================================
		// B -> C
		// =========================================================

		pr('Se actualizará el ranking a productos B sin ventas últimos 90 días');

		$this->db->select('
			p.id,
			p.ps_id,
			p.nombre,
			p.id_ranking,
			p.ultima_venta,
			p.ultima_compra,
			ps.price
		');

		$this->db->from('productos p');

		$this->db->join(
			$dbps . '.ps_product ps',
			'ps.id_product = p.ps_id'
		);

		$this->db->where('p.deleted', 0);

		$this->db->where('p.id_ranking', 3);

		$this->db->where('p.id_categoria', 1);

		$this->db->where('p.fecha_ranking < CURDATE() - INTERVAL 90 DAY');

		$this->db->group_start();
		$this->db->where('p.ultima_venta < CURDATE() - INTERVAL 90 DAY');
		$this->db->or_where('p.ultima_venta IS NULL', null, false);
		$this->db->group_end();

		$this->db->group_start();
		$this->db->where('p.ultima_compra < CURDATE() - INTERVAL 90 DAY');
		$this->db->or_where('p.ultima_compra IS NULL', null, false);
		$this->db->group_end();

		$this->db->where('ps.price <', 70000);

		$productos = $this->db->get()->result_array();

		foreach ($productos as $p) {

			$stockOnline = $getStockOnline($p['id']);

			if ($stockOnline <= 0) {
				continue;
			}

			if ($confirmacion) {
				$this->modifica_ranking($p['id'], 4);
				$this->actualiza_oferta_ps($p['ps_id']);
			}

			$p['stock_online'] = $stockOnline;

			$cambios['B->C'][] = $p;
		}

		// =========================================================
		// A -> B
		// =========================================================

		pr('Se actualizará el ranking a productos A sin ventas últimos 45 días');

		$this->db->select('
			p.id,
			p.ps_id,
			p.nombre,
			p.id_ranking,
			p.ultima_venta,
			p.ultima_compra,
			ps.price
		');

		$this->db->from('productos p');

		$this->db->join(
			$dbps . '.ps_product ps',
			'ps.id_product = p.ps_id'
		);

		$this->db->where('p.deleted', 0);

		$this->db->where('p.id_ranking', 2);

		$this->db->where('p.id_categoria', 1);

		$this->db->where('p.fecha_ranking < CURDATE() - INTERVAL 45 DAY');

		$this->db->group_start();
		$this->db->where('p.ultima_venta < CURDATE() - INTERVAL 45 DAY');
		$this->db->or_where('p.ultima_venta IS NULL', null, false);
		$this->db->group_end();

		$this->db->group_start();
		$this->db->where('p.ultima_compra < CURDATE() - INTERVAL 45 DAY');
		$this->db->or_where('p.ultima_compra IS NULL', null, false);
		$this->db->group_end();

		$this->db->where('ps.price <', 70000);

		$productos = $this->db->get()->result_array();

		foreach ($productos as $p) {

			$stockOnline = $getStockOnline($p['id']);

			if ($stockOnline <= 0) {
				continue;
			}

			if ($confirmacion) {
				$this->modifica_ranking($p['id'], 3);
				$this->actualiza_oferta_ps($p['ps_id']);
			}

			$p['stock_online'] = $stockOnline;

			$cambios['A->B'][] = $p;
		}

		return $cambios;
	}


	public function save_marketplaces($idProducto, $mpActivo, $mpStock) {
		foreach ($mpActivo as $idMarketplace => $activo) {
			$stock = isset($mpStock[$idMarketplace]) && $mpStock[$idMarketplace] !== '' 
				? (int)$mpStock[$idMarketplace] 
				: 0;

			// Si no está activo, el stock debe quedar en 0 sí o sí
			if (!$activo) {
				$stock = 0;
			}
	
			// Consultar si ya existe una entrada activa (no eliminada) para este producto y marketplace
			$this->db->where('id_producto', $idProducto);
			$this->db->where('id_marketplace', $idMarketplace);
			$this->db->where('deleted', 0);
			$existing = $this->db->get('marketplaces_productos')->row();
	
			$data = [
				'id_producto'    => $idProducto,
				'id_marketplace' => $idMarketplace,
				'stock'          => $stock,
				'activo'         => $activo ? 1 : 0
			];
	
			if ($activo) {
				if ($existing && $existing->fecha_creacion !== '0000-00-00 00:00:00') {
					//$data['fecha_creacion'] = $existing->fecha_creacion; // mantener la fecha original
				} else {
					$data['fecha_creacion'] = date('Y-m-d H:i:s'); // nueva fecha de creación
				}
			}
	
			if ($existing) {
				// Actualizar si ya existe
				$this->db->where('id', $existing->id);
				$this->db->update('marketplaces_productos', $data);
			} else {
				// Insertar si no existe
				$this->db->insert('marketplaces_productos', $data);
			}
		}
	
		return true;
	}


	/**
	 * Actualiza el stock del producto en los distintos almacenes
	 * Actualiza disponibles y reservados según ventas pendientes
	 */
	public function actualiza_stocks($id, $almacenes = null, $deseados = null, $deseadosMax = null, $ubicacionAlmacen = null) {
		if (!empty($id)) {
			$reservados = $this->reservados_almacenes($id);
		}
		else {
			// Para productos nuevos trae reservados en 0
			$reservados = $this->reservados_almacenes(-1);
		}

		// Si no es administrador no puede actualizar stock manualmente
		// En ese caso se lee el stock desde DB
		if (empty($almacenes) || empty($deseados)) {
			$stockAlmacenes = $this->stock_almacenes($id);
			foreach ($stockAlmacenes as $k => $v) {
				$almacenes[$k] = $almacenes[$k] ?? $v['stock'];
				$deseados[$k] = $deseados[$k] ?? $v['deseados'];
				$deseadosMax[$k] = $deseadosMax[$k] ?? $v['deseados_max'];
			}
		}

		foreach ($almacenes as $idAlmacen => $stock) {
			// Buscar si ya existe registro para actualizarlo
			$this->db->from('almacenes_productos');
			$this->db->where('id_producto', $id);
			$this->db->where('id_almacen', $idAlmacen);
			$this->db->where('deleted', 0);
			$almacenProducto = $this->db->get()->row_array();

			$idAlmacenProducto = $almacenProducto['id'] ?? null;

			if (!$idAlmacenProducto) {
				$idAlmacenProducto = $this->crear_almacen_producto($id, $idAlmacen);
			}

			// --- NUEVO BLOQUE: calcular en_transito ---
			$this->db->select_sum('preparado', 'en_transito');
			$this->db->from('productos_traspasos pt');
			$this->db->join('traspasos t', 'pt.id_traspaso = t.id');
			$this->db->where('pt.id_producto', $id);
			$this->db->where('t.id_origen', $idAlmacen);
			$this->db->where('t.estado_traspaso', 3); // considerar solo traspasos completados
			$this->db->where('pt.deleted', 0);
			$enTransitoRow = $this->db->get()->row_array();
			$en_transito = intval($enTransitoRow['en_transito'] ?? 0);

			// --- MODIFICADO: actualizar disponibles considerando en_transito ---
			$ap = [
				'stock' => intval($stock),
				'reservados' => $reservados[$idAlmacen] ?? 0,
				'en_transito' => $en_transito,
				'disponibles' => intval($stock) - ($reservados[$idAlmacen] ?? 0) - $en_transito,
			];
			if (isset($ubicacionAlmacen[$idAlmacen])){
				$ap['id_ubicacion'] = $ubicacionAlmacen[$idAlmacen];
			}
			$ap['deseados'] = $deseados[$idAlmacen] ?? 0;
			$ap['deseados_max'] = $deseadosMax[$idAlmacen] ?? 0;
			
			$this->db->where('id', $idAlmacenProducto);
			$this->db->update('almacenes_productos', $ap);
			
			// Guarda Log
			if ($almacenProducto) {
				if ($almacenProducto['stock'] != $ap['stock']) {
					$result['almacenes'][$idAlmacen]['stock'] = [
						'anterior' => $almacenProducto['stock'],
						'actual' => $ap['stock']
					];
				}
				if ($almacenProducto['deseados'] != $ap['deseados']) {
					$result['almacenes'][$idAlmacen]['deseados'] = [
						'anterior' => $almacenProducto['deseados'],
						'actual' => $ap['deseados']
					];
				}
				if ($almacenProducto['deseados_max'] != $ap['deseados_max']) {
					$result['almacenes'][$idAlmacen]['deseados_max'] = [
						'anterior' => $almacenProducto['deseados_max'],
						'actual' => $ap['deseados_max']
					];
				}
			}
			else {
				$result['almacenes'][$idAlmacen] = [
					'stock' => [
						'anterior' => 0,
						'actual' => $ap['stock']
					],
					'deseados' => [
						'anterior' => 0,
						'actual' => $ap['deseados']
					],
					'deseados_max' => [
						'anterior' => 0,
						'actual' => $ap['deseados_max']
					]
				];
			}
		}
		$result['status'] = 'success';
		return $result;
	}

	/**
	 * Cuenta cuantos productos hay reservados por cada almacen
	 * Considera las ventas pendientes
	 * @return array con id almacen y cantidad reservados
	 */
	public function reservados_almacenes($idProducto = 0) {
		// Si viene -1 o 0, devolvemos todo en 0 para todos los almacenes
		if (empty($idProducto) || intval($idProducto) < 1) {
			$almacenes = $this->db->get_where('almacenes', ['deleted' => 0])->result_array();
			$empty = [];
			foreach ($almacenes as $a) {
				$empty[$a['id']] = 0;
			}
			return $empty;
		}

		$idProducto = intval($idProducto);

		// Inicializamos con todos los almacenes en 0
		$almacenes = $this->db->get_where('almacenes', ['deleted' => 0])->result_array();
		$reservados = [];
		foreach ($almacenes as $a) {
			$reservados[$a['id']] = 0;
		}

		// Query real de reservas
		$this->db->select('pv.cantidad, t.id_almacen');
		$this->db->from('productos_ventas pv');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'left');
		$this->db->join('transportistas t', 'v.id_transportista = t.id', 'left');

		$this->db->where('pv.id_producto', $idProducto);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where('t.deleted', 0);
		$this->db->where('v.reservado', 1);
		$this->db->where('v.id_tipo_venta', 1);

		$ventas = $this->db->get()->result_array();

		// Completar valores
		foreach ($ventas as $v) {
			$a = $v['id_almacen'];
			$reservados[$a] += (int)$v['cantidad'];
		}

		return $reservados;
	}


	public function sugerencias($periodo = 15, $idCategoria = 0, $idProveedor = 0, $rankings = [0, 1, 2, 3]) {
		$this->db->select("
			p.id,
			p.ps_id,
			p.nombre,
			p.disponibles,
			p.id_categoria,
			p.id_proveedor,
			p.id_ranking,
			p.tipo_reposicion,

			-- Lo vendido en el periodo visible
			IFNULL(SUM(
				CASE 
					WHEN v.fecha_venta BETWEEN CURDATE() - INTERVAL {$periodo} DAY AND CURDATE() + INTERVAL 1 DAY 
					THEN pv.cantidad 
					ELSE 0 
				END
			), 0) AS vendidos_periodo,

			-- Lo vendido en los últimos 30 días (regla fija)
			IFNULL(SUM(
				CASE 
					WHEN v.fecha_venta BETWEEN CURDATE() - INTERVAL 30 DAY AND CURDATE() + INTERVAL 1 DAY 
					THEN pv.cantidad 
					ELSE 0 
				END
			), 0) AS vendidos_30
		", false);

		$this->db->from('productos p');
		$this->db->join('productos_ventas pv','p.id = pv.id_producto', 'left');
		$this->db->join('ventas v','pv.id_venta = v.id', 'left');

		$this->db->where('p.disponibles >= 0'); // evita preventa
		$this->db->where('p.deleted', 0);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where_in('p.id_ranking', $rankings);

		// No reponer nunca
		$this->db->where('p.tipo_reposicion !=', -1);

		if (!empty($idCategoria)) {
			$this->db->where('p.id_categoria', $idCategoria);
		}

		if (!empty($idProveedor)) {
			$this->db->where('p.id_proveedor', $idProveedor);
		}

		$this->db->group_by('p.id');

		// Lógica por tipo de reposición
		$this->db->having("
			(
				p.tipo_reposicion = 0 
				AND vendidos_30 > 0 
				AND p.disponibles < vendidos_30
			)
			OR
			(
				p.tipo_reposicion = 1 
				AND vendidos_30 > 0 
				AND p.disponibles < vendidos_30
			)
			OR
			(
				p.tipo_reposicion = 2 
				AND vendidos_30 > 0 
				AND p.disponibles < (vendidos_30 * 2)
			)
			OR
			(
				p.tipo_reposicion = 3 
				AND vendidos_30 > 0 
				AND p.disponibles < (vendidos_30 * 3)
			)
		", null, false);

		$this->db->order_by('vendidos_30', 'DESC');

		$result = $this->db->get()->result_array();

		// Calcular recomendación en PHP (más claro y controlable)
		foreach ($result as &$p) {
			$recomendado = 0;

			if ($p['tipo_reposicion'] == 1) {
				$objetivo = $p['vendidos_30'] * 2;
				$recomendado = max(0, $objetivo - $p['disponibles']);
			}
			elseif ($p['tipo_reposicion'] == 2) {
				$objetivo = $p['vendidos_30'] * 3;
				$recomendado = max(0, $objetivo - $p['disponibles']);
			}
			elseif ($p['tipo_reposicion'] == 3) {
				$objetivo = $p['vendidos_30'] * 4;
				$recomendado = max(0, $objetivo - $p['disponibles']);
			}

			$p['recomendado_compra'] = $recomendado;
		}

		return $result;
	}
/*
	public function sugerencias($periodo = 15, $idCategoria = 0, $idProveedor = 0, $rankings = [0, 1, 2, 3]) {

		$this->db->select('p.id, p.ps_id, p.nombre, p.disponibles, p.id_categoria, p.id_proveedor, p.id_ranking, p.tipo_reposicion, SUM(pv.cantidad) as vendidos_quincena');
		$this->db->from('productos p');
		$this->db->join('productos_ventas pv','p.id = pv.id_producto', 'left');
		$this->db->join('ventas v','pv.id_venta = v.id', 'left');
		$this->db->where('v.fecha_venta BETWEEN CURDATE() - INTERVAL ' . $periodo . ' DAY AND CURDATE() + INTERVAL 1 DAY');
		$this->db->where('p.disponibles >= 0'); // Evita mostrar productos en preventa
		$this->db->where('p.deleted', 0);
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		$this->db->where_in('p.id_ranking', $rankings);
		if (!empty($idCategoria)) {
			$this->db->where('p.id_categoria', $idCategoria);
		}
		if (!empty($idProveedor)) {
			$this->db->where('p.id_proveedor', $idProveedor);
		}
		$this->db->group_by('pv.id_producto');
		// Solo productos que se haya vendido más de 1
		$this->db->having('vendidos_quincena >', 1);
		$this->db->having('p.disponibles <', 'vendidos_quincena', false);
		$this->db->order_by('vendidos_quincena', 'DESC');

		return $this->db->get()->result_array();
	}
	*/


	public function delete($id) {
		//revisar si viene id y si viene se hace update
		if (!empty($id)) {
			$this->db->set('deleted', 1, FALSE);
			$this->db->where('id', $id);
			$this->db->update('productos');
		}
	}


	public function guarda_codigo($post){
		$difs['codigo_barra'] = [
			'anterior' => '',
			'actual' => $post['codigo']
		];

		$log = [
			'tipo_entidad' => 1,
			'id_entidad' => $post['id'],
			'id_usuario' => $_SESSION['usuario']['id'],
			'data' => json_encode($difs)
		];
		$this->db->insert('cambios_log', $log);
		
		$this->db->set('codigo_barra', '"' . $post['codigo'] . '"', FALSE);
		$this->db->where('id', $post['id']);
		$return = $this->db->update('productos');
		return $return;
	}

	/**
	 * Función que agrega y quita productos de categoría ofertas según tiene o no descuentos
	 */
	public function actualiza_categoria_ofertas() {
		$idCategory = 98;
		$cambios = [];

		$ps = $this->load->database('prestashop', TRUE);

		// Remueve productos de Ofertas
		// buscamos todos los productos de la categoría oferta que no tengan ningún SpecifiPrice o no tengan stock
		$ps->select('p.id_product');
		$ps->from('ps_product p');
		$ps->join('ps_category_product cp','p.id_product = cp.id_product AND cp.id_category = ' . $idCategory, 'left');
		$ps->join('ps_specific_price sp','p.id_product = sp.id_product AND sp.id_shop = 1 AND from <= NOW() AND to >= NOW()', 'left');
		$ps->join('ps_stock_available sa','p.id_product = sa.id_product AND sp.id_shop = 1', 'left');
		$ps->where('cp.id_category IS NOT NULL');

		$ps->group_start();
			$ps->or_where('sp.id_specific_price IS NULL');
			$ps->or_where('sa.quantity < 1');
		$ps->group_end();

		$ps->group_by('p.id_product');
		$ps->order_by('p.id_product ASC');

		$currents = $ps->get()->result_array();

		foreach ($currents as $current) {
			$result = $this->actualiza_oferta_ps($current['id_product']);
			if ($result['status']) {
				$cambios[$current['id_product']] = $result['status'];
			}
		}

		// Agrega productos a ofertas
		// Buscamos todos los productos que no tengan la categoría oferta y tengan specific prices y stock
		$ps->select('p.id_product');
		$ps->from('ps_product p');
		$ps->join('ps_category_product cp','p.id_product = cp.id_product AND cp.id_category = ' . $idCategory, 'left');
		$ps->join('ps_specific_price sp','p.id_product = sp.id_product AND sp.id_shop = 1 AND from <= NOW() AND to >= NOW()', 'left');
		$ps->join('ps_stock_available sa','p.id_product = sa.id_product AND sp.id_shop = 1', 'left');
		$ps->where('cp.id_category IS NULL');
		$ps->where('sp.id_specific_price IS NOT NULL');
		$ps->where('sa.quantity > 0');
		$ps->group_by('p.id_product');
		$ps->order_by('p.id_product ASC');

		$news = $ps->get()->result_array();

		foreach ($news as $new) {
			$result = $this->actualiza_oferta_ps($new['id_product']);
			if ($result['status']) {
				$cambios[$new['id_product']] = $result['status'];
			}
		}

		return $cambios;
	}


	/**
	 * Función que registra y quita productos de categoría recien llegados según tiempo en categoría
	 */
	public function actualiza_recien_llegados() {
		$idCategory = 110;
		$cambios = [];
		// Fecha actual
		$fechaActual = date("Y-m-d");
		// Fecha hace 20 días
		$hace20Dias = date("Y-m-d", strtotime("-20 days"));

		$ps = $this->load->database('prestashop', TRUE);
		$dbv = (ENVIRONMENT == 'production') ? 'dementeg_ventas' : 'ventas-dmg';

		// Busca todos los productos que se han agregado a la categoría y no se han registrado en la tabla
		$ps->select('p.id_product');
		$ps->from('ps_product p');
		$ps->join('ps_category_product cp','p.id_product = cp.id_product AND cp.id_category = ' . $idCategory, 'left');
		$ps->join($dbv.'.recien_llegados rl','p.id_product = rl.ps_id_producto AND rl.deleted = 0', 'left');
		$ps->where('cp.id_category IS NOT NULL');
		$ps->where('rl.ps_id_producto IS NULL');
		$ps->group_by('p.id_product');
		$ps->order_by('p.id_product ASC');

		$nuevasLlegadas = $ps->get()->result_array();

		// Agrega los nuevos productos a la tabla de recien_llegados
		if (!empty($nuevasLlegadas)) {
			foreach ($nuevasLlegadas as $p) {
				$recienLlegado = [
					'ps_id_producto' => $p['id_product'],
					'fecha_agregado' => $fechaActual
				];
				$this->db->insert('recien_llegados', $recienLlegado);
				$cambios['Agregados'][] = $p['id_product'];
			}
		}
		else {
			pr('No se encontraron nuevos productos Recién Llegados');
		}

		// Consulta para seleccionar los registros de la tabla "recien_llegados" que tienen al menos 20 días
		$this->db->from('recien_llegados');
		$this->db->where('fecha_agregado <= "' . $hace20Dias . '"');
		$this->db->where('deleted', 0);

		$vencidos = $this->db->get()->result_array();

		if (!empty($vencidos)) {
			foreach($vencidos as $v) {
				$ps->where('id_product', $v['ps_id_producto']);
				$ps->where('id_category', $idCategory);
				$ps->delete('ps_category_product');

				$borrar['deleted'] = 1;
				$this->db->where('id', $v['id']);
				$this->db->update('recien_llegados', $borrar);

				$cambios['Eliminados'][] = $v['ps_id_producto'];
			}
		}
		else {
			pr('No se encontraron productos vencidos en Recién Llegados');
		}

		return $cambios;
	}

	/**
	 * Busca precios en PS y los actualiza a VDM, registra cambios en log
	 */
	public function actualiza_precios() {
		// Busca todos los productos con stock en VDM
		$this->db->select('id, ps_id, precio, precio_oferta');
		$this->db->from('productos');
		$this->db->where('disponibles >', 0);
		$this->db->where('deleted', 0);
		$this->db->where('ps_id !=', '0');

		$productos = $this->db->get()->result_array();
		$c = 0;
		$actualizados = [];

		// Foreach consultando su precio en PS
		foreach($productos as $p) {
			$psPrice = $this->ps_prices($p['ps_id'], 1);
			//Setea precio oferta de VDM
			if ($p['precio_oferta'] == 0) {
				$p['precio_oferta'] = $p['precio'];
			}

			if (!empty($psPrice['precio_normal'])) {
				// Compara precios
				if ($p['precio'] != $psPrice['precio_normal'] || $p['precio_oferta'] != $psPrice['precio']) {
					// Si se identifican cambios se actualiza registro en VDM
					$newP['precio'] = $psPrice['precio_normal'];
					$newP['precio_oferta'] = $psPrice['precio'];
					if ($psPrice['precio_normal'] == $psPrice['precio']) {
						$newP['precio_oferta'] = 0;
					}
					$this->db->where('id', $p['id']);
					$this->db->update('productos', $newP);

					// Se guarda en log de cambio de precios
					$newLog = [
						'id_producto' => $p['id'],
						'precio_normal_anterior' => $p['precio'],
						'precio_final_anterior' => $p['precio_oferta'],
						'precio_normal' => $psPrice['precio_normal'],
						'precio_final' => $psPrice['precio']
					];
					$this->db->insert('precios_log', $newLog);
					$actualizados[$newLog['id_producto']] = $newLog;
					$c++;
				}
			}
		}
		$result['actualizados'] = $actualizados;

		return $result;
	}

	/**
	 * Entrega registros de Log del producto
	 */
	public function get_log($id) {
		$this->db->select("*");
		$this->db->from('cambios_log');
		$this->db->where('tipo_entidad', 1);
		$this->db->where('id_entidad', $id);
		$this->db->order_by('created', 'DESC');
		$tmp = $this->db->get()->result_array();
		foreach ($tmp as &$l) {
			$l['cambios'] = json_decode($l['data'], TRUE);
		}
		return $tmp;
	}

	/*******************************************************************************************/
	/*                    Consultas no relacionadas solo a productos                           */
	/*******************************************************************************************/

	/**
	 * Retorna los datos necesarios para administrar los productos
	 */
	public function get_globales($data){
		$data['ps_productos'] = $this->ps_list();
		$data['ps_imagenes'] = $this->ps_images();
		$data['rankings'] = $this->list_rankings();

		$CI =& get_instance();
		$CI->load->model('proveedor_model', 'Proveedor');
		$data['proveedores'] = $this->Proveedor->lista();
		$data['distribuidores'] = $this->Proveedor->lista(true);

		$CI->load->model('categoria_model', 'Categoria');
		$data['categorias'] = $this->Categoria->list();

		$CI->load->model('almacen_model', 'Almacen');
		$data['almacenes'] = $this->Almacen->lista(false);
		$data['almacenes_activos'] = $this->Almacen->lista(true);
		$data['ubicaciones'] = $this->Almacen->ubicaciones();

		$CI->load->model('marketplace_model');
		$data['marketplaces'] = $CI->marketplace_model->lista_marketplaces();
		$data['marketplaces_detalles'] = $CI->marketplace_model->get_marketplaces();

		return $data;
	}

	/*******************************************************************************************/
	/*                              Funciones de Consulta PS                                   */
	/*******************************************************************************************/

	/**
	 * Entrega datos de producto en PS
	 */
	public function get_ps($id) {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name, p.price");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps->where('p.id_product', $id);
		$ps_product = $ps->get()->row_array();

		$return['ps_id'] = $ps_product['id_product'];
		$return['nombre'] = $ps_product['name'];
		$return['precio'] = intval($ps_product['price']);

		return $return;
	}

	/**
	 * Entrega lista de los productos de ps
	 */
	public function ps_list() {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps_products = $ps->get()->result_array();
		$return = [];
		foreach($ps_products as $ps_product) {
			$return[$ps_product['id_product']] = $ps_product['name'];
		}
		return $return;
	}

	/**
	 * Entrega lista de los productos de ps que no están en Ventas
	 */
	public function ps_news() {
		$this->db->select('ps_id');
		$this->db->from('productos');
		$this->db->where('deleted', 0);
		$this->db->where('ps_id !=', 0);
		$this->db->order_by('created', 'DESC');
		$productos = $this->db->get()->result_array();

		$imported = [];
		foreach ($productos as $p) {
			$imported[] = $p['ps_id'];
		}

		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps->where_not_in('p.id_product', $imported);
		$ps->order_by('p.id_product', 'desc');
		$ps_products = $ps->get()->result_array();
		$return = [];
		foreach($ps_products as $ps_product) {
			$return[$ps_product['id_product']] = $ps_product['name'];
		}

		return $return;
	}

	/**
	 * Entrega fechas de los productos de ps que están en preventa
	 */
	public function ps_dates() {
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select("p.id_product, l.name, p.available_date, l.available_now, l.available_later");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps->where('p.active', 1);
		$ps->where('p.available_date !=', '0000-00-00');
		$ps->order_by('p.available_date', 'ASC');
		$ps->order_by('p.id_product', 'ASC');
		$ps_products = $ps->get()->result_array();

		return $ps_products;
	}

	/**
	 * Entrega listado de productos con suscripciones
	 */
	public function ps_waiting($tipo = 0) {
		if ($tipo == 0) {
			$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';
			$this->db->select('p.id, m.id_product, l.name, COUNT(m.id_product) as cant');
			$this->db->from($dbps.'.ps_mailalert_customer_oos m');
			$this->db->join('productos p', 'ON p.ps_id = m.id_product', 'left');
			$this->db->join($dbps.'.ps_product_lang l','l.id_product = m.id_product', 'left');
			$this->db->group_by('p.id, m.id_product, l.name');
			$this->db->order_by('cant desc');
			
			$ps_products = $this->db->get()->result_array();

			$waiting = [];
			foreach ($ps_products as $p) {
				$waiting['products'][$p['id_product']] = $p;
			}

			$ps = $this->load->database('prestashop', TRUE);
			$ps->select('m.id_product as id_product, m.customer_email, m.created, c.id_customer, c.firstname, c.lastname');
			$ps->from('ps_mailalert_customer_oos m');
			$ps->join('ps_customer c','m.id_customer = c.id_customer', 'left');
			$ps->order_by('m.created DESC');
			$ps_mails = $ps->get()->result_array();

			$waiting['count'] = count($ps_mails);

			foreach ($ps_mails as $m) {
				$nombre = (!empty($m['id_customer']) ? $m['firstname'] . ' ' . $m['lastname'] : '');
				$waiting['products'][$m['id_product']]['mails'][] = [
					'nombre' => $nombre,
					'mail' => $m['customer_email'],
					'fecha' => $m['created']
				];
			}
		}
		if ($tipo == 1) {
			$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';
			$this->db->select('p.id, m.id_product, l.name, m.created, m.customer_email as mail, m.created as fecha, c.id_customer, c.firstname, c.lastname');
			$this->db->from($dbps.'.ps_mailalert_customer_oos m');
			$this->db->join('productos p', 'ON p.ps_id = m.id_product', 'left');
			$this->db->join($dbps.'.ps_product_lang l','l.id_product = m.id_product', 'left');
			$this->db->join($dbps.'.ps_customer c','m.id_customer = c.id_customer', 'left');
			$this->db->order_by('m.created DESC');
			$this->db->limit('200');

			$ps_alertas = $this->db->get()->result_array();

			$waiting['alertas'] = [];

			foreach ($ps_alertas as $m) {
				$m['nombre'] = (!empty($m['id_customer']) ? $m['firstname'] . ' ' . $m['lastname'] : '');
				$waiting['alertas'][] = $m;
			}
		}

		return $waiting;
	}

	/**
	 * Entrega listado de productos con categorías de descuento
	 */
	public function ps_products($categoies = []) {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select('p.id_product as id, l.name, p.price, p.active');
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->order_by('p.active DESC');
		$ps->order_by('l.name ASC');
		$tmp = $ps->get()->result_array();

		$products = [];
		foreach ($tmp as $p) {
			$products[$p['id']] = $p;
		}
		unset($tmp);

		$ps->select('id_category, id_product, position');
		$ps->from('ps_category_product');
		if (!empty($categories)) {
			$ps->where_in('id_category', $keys($categories));
		}
		$ps->order_by('id_product ASC');
		$tmp = $ps->get()->result_array();

		foreach ($tmp as $c) {
			$products[$c['id_product']]['categories'][$c['id_category']] = $c['position'];
		}

		return $products;
	}

	/**
	 * Entrega listado de categorías prestashop
	 * @param boolean $discounts entrega solo categorias que aplican descuentos
	 */
	public function ps_categories($discounts = false) {
		$cs = [20, 21, 22, 23, 24];

		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('c.id_category, l.name');
		$ps->from('ps_category c');
		$ps->join('ps_category_lang l','l.id_category = c.id_category');
		if ($discounts) {			
			$ps->where_in('c.id_category', $cs);
		}
		$ps->order_by('c.position ASC');
		$tmp = $ps->get()->result_array();

		foreach ($tmp as $c) {
			$categories[$c['id_category']] = $c['name'];
		}

		return $categories;
	}

	/**
	 * Entrega id de prestashop de un producto
	 * @param int $id Id de producto en Ventas
	 */
	public function get_ps_id($id = 0) {
		// Busca id de producto en PS
		$this->db->select('ps_id');
		$this->db->from('productos');
		$this->db->where('id', $id);
		$p = $this->db->get()->row_array();
		return $p['ps_id'] ?? 0;
	}

	public function get_ps_image($ps_id = 0) {
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('id_image');
		$ps->from('ps_image');
		$ps->where('id_product', $ps_id);
		$ps->where('cover', 1);
		$tmp = $ps->get()->row_array();
		$result = 0;
		if (!empty($tmp)) {
			$result = $tmp['id_image'];
		}
		return $result;
	}

	public function get_ps_images($ps_id = 0) {
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('id_image');
		$ps->from('ps_image');
		$ps->where('id_product', $ps_id);
		$ps->order_by('position', 'ASC');
		$tmp = $ps->get()->result_array();

		$result = [];

		if (!empty($tmp)) {
			foreach ($tmp as $i) {
				$result[] = $i['id_image'];
			}
		}
		return $result;
	}

	public function ps_images() {
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('id_product, id_image');
		$ps->from('ps_image');
		$ps->where('cover', 1);
		$productos = $ps->get()->result_array();
		$result = [];
		if (!empty($productos)) {
			foreach ($productos as $p) { 
			$result[$p['id_product']] = $p['id_image'];
			}
		}
		return $result;
	}

	public function ps_monitor($seccion) {
		$ps = $this->load->database('prestashop', TRUE);
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';
		$invendibles = [7, 10, 11, 12];

		switch ($seccion) {
			case  'diferencias-stock':
				$this->db->select('p.id,sa.id_product,p.nombre as name,p.stock,sa.physical_quantity as ps_stock,p.disponibles,sa.quantity as ps_disponibles,p.reservados,sa.reserved_quantity as ps_reservados, pl.available_now, pl.available_later, cp.id_category');
				$this->db->from('productos p');
				$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product');
				$this->db->join($dbps.'.ps_product_lang pl','ON p.ps_id = pl.id_product');
				$this->db->join($dbps.'.ps_category_product cp','ON cp.id_product = pl.id_product AND cp.id_category in (10, 14, 15, 18, 52, 116)');
				$this->db->where('deleted', 0);
				$this->db->where('pl.available_now','');
				$this->db->where('pl.available_later','');
				$this->db->where('p.ignora_descuadre', 0);
				$this->db->where('p.disponibles != sa.quantity');
				$this->db->order_by('cp.id_category ASC');
				$this->db->order_by('pl.name ASC');
				$diferencias = $this->db->get()->result_array();

				$CI =& get_instance();
				$CI->load->model('almacen_model', 'Almacen');

				foreach ($diferencias as $diferencia) {
					// Filtrar los que tienen productos dañados
					$stockOnline = $this->Almacen->stock_online($diferencia['id']);
					
					if ($stockOnline != $diferencia['ps_disponibles']) {
						$diferencia['stock_online'] = $stockOnline;
						if (!empty($diferencia['id_category'])) {
							$difs[$diferencia['id_category']][] = $diferencia;
						}
						else{
							$difs[0][] = $diferencia;
						}
					}
				}

				$result['diferencias'] = $difs;
				break;


			case 'sin-disponibilidad':
				$ps->select('p.id_product, pl.name, sa.quantity');
				$ps->from('ps_product p');
				$ps->join('ps_stock_available sa','p.id_product = sa.id_product','left');
				$ps->join('ps_product_lang pl','p.id_product = pl.id_product','left');
				$ps->join('ps_category_product cp','p.id_product = cp.id_product','left');
				$ps->join('ps_feature_product fp','p.id_product = fp.id_product AND fp.id_feature = 19', 'left');
				$ps->where('p.active', 1);
				$ps->where('sa.quantity > 0');
				$ps->where('pl.id_lang', 1);
				$ps->where('cp.id_category', 10);
				$ps->where('fp.id_feature_value IS NULL');
				$ps->order_by('sa.quantity','DESC');
				$result['disponibilidad'] = $ps->get()->result_array();
				break;


			case 'sin-video':
				$ps = $this->load->database('prestashop', TRUE);
				$ps->select('p.id_product, pl.name, sa.quantity');
				$ps->from('ps_product p');
				$ps->join('ps_stock_available sa','p.id_product = sa.id_product','left');
				$ps->join('ps_product_lang pl','p.id_product = pl.id_product','left');
				$ps->join('ps_category_product cp','p.id_product = cp.id_product','left');
				$ps->join('ps_feature_product fp','p.id_product = fp.id_product AND fp.id_feature = 12', 'left');
				$ps->where('p.active', 1);
				$ps->where('sa.quantity > 0');
				$ps->where('pl.id_lang', 1);
				$ps->where('cp.id_category', 10);
				$ps->where('fp.id_feature_value IS NULL');
				$ps->order_by('sa.quantity','DESC');
				$result['video'] = $ps->get()->result_array();
				break;


			case 'sin-marca':
				$ps->select('ps.id_product, pl.name, ps.available_date, cp.id_category, sa.quantity');
				$ps->from('ps_product_shop ps');
				$ps->join('ps_product_lang pl','ps.id_product = pl.id_product','left');
				$ps->join('ps_product p','ps.id_product = p.id_product','left');
				$ps->join('ps_category_product cp','ON cp.id_product = pl.id_product AND cp.id_category in (10, 14, 15, 18, 52, 116)');
				$ps->join('ps_stock_available sa','p.id_product = sa.id_product','left');
				$ps->where('p.active', 1);
				$ps->where('p.id_manufacturer', 0);
				$ps->where('pl.id_lang', 1);
				$ps->order_by('sa.quantity','DESC');
				$sinMarcas = $ps->get()->result_array();

				foreach ($sinMarcas as $sinMarca) {
					if (!empty($sinMarca['id_category'])) {
						$sin[$sinMarca['id_category']][] = $sinMarca;
					}
					else{
						$sin[0][] = $sinMarca;
					}
				}
				$result['sin_marca'] = $sin;
				break;


			case 'sin-descripcion':
				//Productos sin Descripción
				$this->db->select('p.id, ps_id, p.id_categoria, p.nombre, p.disponibles');
				$this->db->from('productos p');
				$this->db->where('p.deleted', 0);
				$this->db->where('descripcion', '');
				$this->db->where('p.disponibles >', 0);
				$this->db->where_not_in('p.id_categoria', $invendibles);
				$this->db->order_by('p.id_categoria ASC');
				$this->db->order_by('p.disponibles DESC');
				$indescritos = $this->db->get()->result_array();

				foreach($indescritos as $indescrito) {
					$result['indescritos'][$indescrito['id_categoria']][] = $indescrito;
				}

				break;

			case 'fechas-llegada':
				$ps->select('ps.id_product, pl.name, ps.available_date');
				$ps->from('ps_product_shop ps');
				$ps->join('ps_product_lang pl','ps.id_product = pl.id_product','left');
				$ps->join('ps_product p','ps.id_product = p.id_product','left');
				$ps->where('p.active', 1);
				$ps->where('ps.available_date != "0000-00-00"');
				$ps->where('pl.id_lang', 1);
				$ps->order_by('ps.available_date','ASC');
				$result['llegadas'] = $ps->get()->result_array();
				break;


			case 'sin-deseados-italia':
			case 'sin-deseados-dominicos':
			case 'sin-deseados-drugstore':

				// Mapeo de secciones a almacenes y key_result
				$almacenes = [
					'sin-deseados-italia'       => ['id' => 5,  'key' => 'deseados_1601'],
					'sin-deseados-dominicos'    => ['id' => 8,  'key' => 'deseados_dominicos'],
					'sin-deseados-drugstore'    => ['id' => 12, 'key' => 'deseados_drugstore']
				];

				// Solo tomamos el almacén correspondiente a la sección actual
				$id_almacen   = $almacenes[$seccion]['id'];
				$key_result   = $almacenes[$seccion]['key'];

				$this->db->select('p.id, ps_id, p.id_categoria, p.nombre, p.disponibles, ap.deseados');
				$this->db->from('productos p');
				$this->db->join(
					'almacenes_productos ap',
					"p.id = ap.id_producto AND id_almacen = {$id_almacen} AND ap.deleted = 0",
					'left'
				);
				$this->db->where('p.deleted', 0);
				$this->db->where('deseados', 0);
				$this->db->where('p.disponibles >', 0);
				$this->db->where_not_in('p.id_categoria', $invendibles);
				$this->db->order_by('p.id_categoria ASC');
				$this->db->order_by('p.disponibles DESC');

				$deseados = $this->db->get()->result_array();

				foreach ($deseados as $deseado) {
					$result[$key_result][$deseado['id_categoria']][] = $deseado;
				}

				break;

			case 'agotados-online':
				$result = $this->agotados_online();
				break;
		}

		// Productos con categoría preventa sin fecha de llegada
		// POR HACER


		/* YA NO SE USAN
		//Productos con unidades físicas sin unidades disponibles (Todos vendidos)
		$this->db->select('*, ps_id as id_product, nombre as name');
		$this->db->from('productos p');
		$this->db->where('deleted', 0);
		$this->db->where('disponibles', 0);
		$this->db->where('reservados >', 0);
		$this->db->order_by('nombre ASC');
		$agotados = $this->db->get()->result_array();

		foreach($agotados as &$agotado) {
			$agotado['almacenes'] = $this->stock_almacenes($agotado['id']);
		}

		$result['agotados'] = $agotados;
		*/


		//Categorías que no se pueden vender: Insumos, Demos, Virtuales
		

		//Productos con solo 1 unidad disponible y reservados
		/*
		$this->db->select('*, ps_id as id_product, nombre as name');
		$this->db->from('productos p');
		$this->db->where('deleted', 0);
		$this->db->where('disponibles', 1);
		$this->db->where_not_in('id_categoria', $invendibles);
		$this->db->order_by('nombre ASC');
		$ultimo = $this->db->get()->result_array();
		$result['ultimo'] = $ultimo;
		*/

		return $result;
	}

	/*
	 * Entrega lista de precios publicados en PS aplicando los descuentos correspondientes
	 * Si no se indica ps_id se entregará la info de todos
	 * Si se indica detailed = 0 solo entrega el precio final
	 * Si se indica detailed = 1 entrega prwecio normal, dcto y precio final
	 */
	public function ps_prices($ps_id = 0, $detailed = 0) {
		$ps_prices = [];
		// Busca precios en PS
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name, p.price, sp.id_specific_price_rule, sp.reduction, sp.reduction_type, sp.from, sp.to");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->join('ps_specific_price sp','p.id_product = sp.id_product AND sp.id_shop = 1', 'left');
		$ps->where('l.id_lang', 1);

		// Excluir descuentos con id_specific_price_rule = 58 // Black Friday
		$ps->where('(sp.id_specific_price_rule IS NULL OR sp.id_specific_price_rule != 58)');

		if (!empty($ps_id)) {
			$ps->where('p.id_product', $ps_id);
		}

		$ps->order_by('p.id_product ASC, sp.id_specific_price_rule ASC');
		$ps_products = $ps->get()->result_array();

		$vacio = '0000-00-00 00:00:00';
		$ahora = strtotime(date('Y-m-d H:i:s'));


		// Por cada descuento busca si cumple con las fechas
		// Solo se puede aplicar 1 descuento a cada producto, tomará el descuento válido más alto
		foreach ($ps_products as $psp) {
			$idProd = $psp['id_product'];

			if (empty($ps_prices[$idProd])) {
				$precioNormal = round($psp['price']);
				$precio = $precioNormal;
				$precioTienda = $precioNormal;
				$descuento = 0;
				$descuentoTienda = 0;

				// Filtrar descuentos válidos para este producto
				$validDiscounts = [];
				foreach ($ps_products as $psp2) {
					if ($psp2['id_product'] == $idProd && !empty($psp2['reduction'])) {
						$valido = true;

						// Fecha inicio
						if ($psp2['from'] != $vacio && $ahora < strtotime($psp2['from'])) {
							$valido = false;
						}

						// Fecha fin
						if ($psp2['to'] != $vacio && $ahora > strtotime($psp2['to'])) {
							$valido = false;
						}

						if ($valido) {
							$validDiscounts[] = $psp2;
						}
					}
				}

				// Elegir descuento válido (el de mayor reducción)
				if (!empty($validDiscounts)) {
					// Elegir el mayor descuento:
					usort($validDiscounts, function ($a, $b) {
						$descA = ($a['reduction_type'] == 'percentage') ? $a['price'] * $a['reduction'] : $a['reduction'];
						$descB = ($b['reduction_type'] == 'percentage') ? $b['price'] * $b['reduction'] : $b['reduction'];
						return $descB <=> $descA;
					});

					$chosen = $validDiscounts[0];

					if ($chosen['reduction_type'] == 'amount') {
						$descuento = round($chosen['reduction']);
						$precio = round($precioNormal - $descuento);
					} elseif ($chosen['reduction_type'] == 'percentage') {
						$descuento = round($precioNormal * $chosen['reduction']);
						$precio = round($precioNormal - $descuento);
					}

					$precioTienda = $precio;
					$descuentoTienda = $descuento;
				}

				// Guardar resultado
				if ($detailed == 0) {
					$ps_prices[$idProd] = $precio;
				} else {
					$ps_prices[$idProd] = [
						'precio_normal'   => $precioNormal,
						'descuento'       => $descuento,
						'precio'          => $precio,
						'descuento_tienda'=> $descuentoTienda,
						'precio_tienda'   => $precioTienda
					];
				}
			}
		}

		// Si se pidió solo un precio se devuelve el precio de PS
		if (!empty($ps_id)) {
			if (!empty($ps_prices[$ps_id])) {
				return $ps_prices[$ps_id];
			}
			else {
				return 0;
			}
		}


		return $ps_prices;
	}

	/*
	 * Entrega stock disponible en PS
	 */
	public function ps_stock($ps_id = 0) {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("quantity");
		$ps->from('ps_stock_available');
		$ps->where('id_product', $ps_id);
		$ps->where('id_shop', 1);
		$ps_product = $ps->get()->row_array();

		if (!empty($ps_product)) {
			return $ps_product['quantity'];
		}

		return 0;
	}

	/**
	 * Entrega estado de producto y fecha de llegada
	 */
	public function ps_state($ps_id = 0){
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('p.ps_id, p.disponibles, psp.active, psp.available_date, sa.id_product, sa.physical_quantity as ps_stock, sa.quantity as ps_disponibles, pl.available_now, pl.available_later');
		$this->db->from('productos p');
		$this->db->join($dbps.'.ps_product psp','ON p.ps_id = psp.id_product', 'left');
		$this->db->join($dbps.'.ps_stock_available sa','ON p.ps_id = sa.id_product', 'left');
		$this->db->join($dbps.'.ps_product_lang pl','ON p.ps_id = pl.id_product', 'left');
		$this->db->where('p.deleted', 0);
		$this->db->where('p.ps_id', $ps_id);

		$producto = $this->db->get()->row_array();

		$estado['active'] = $producto['active'];

		if($producto['disponibles'] > 0) {
			$estado['estado'] = 'Disponible';
		}
		elseif($producto['disponibles'] < 1 && $producto['available_now'] == 'Preventa') {
			$estado['estado'] = 'Preventa';
			$estado['fecha_disponible'] = $producto['available_date'];
		}
		else {
			$estado['estado'] = 'Agotado';
		}

		if ($producto['available_now'] == 'Preventa') {
			$estado['ps_state'] = 'Preventa';
		}
		elseif ($producto['ps_stock'] > 0) {
			$estado['ps_state'] = 'Disponible';
		}
		else {
			$estado['ps_state'] = 'Agotado';
		}


		return $estado;
	}


	public function es_preventa_ps($ps_id)
	{
		$dbps = (ENVIRONMENT == 'production') ? 'dementeg_presta' : 'dementelocal';

		$this->db->select('sa.quantity as ps_disponibles, pl.available_now');
		$this->db->from($dbps.'.ps_stock_available sa');
		$this->db->join($dbps.'.ps_product_lang pl', 'pl.id_product = sa.id_product', 'left');

		$this->db->where('sa.id_product', $ps_id);
		$this->db->where('pl.id_lang', 1);

		$p = $this->db->get()->row_array();

		if (!$p) {
			return false;
		}

		// Solo es preventa si la etiqueta está puesta Y no hay stock
		if ($p['available_now'] === 'Preventa' && $p['ps_disponibles'] <= 0) {
			return true;
		}

		return false;
	}

	/*******************************************************************************************/
	/*                              Funciones de Actualización                                 */
	/*******************************************************************************************/

	/**
	 * Actualiza la ultima fecha de compra y de venta de cada producto
	 */
	public function update_dates($id = null) {
		//Busca la ultima fecha de VENTA de cada producto
		$this->db->select('pv.id_producto, MAX(fecha_venta) as fecha_venta');
		$this->db->from('productos_ventas pv');
		$this->db->join('ventas v', 'pv.id_venta = v.id', 'left');
		$this->db->where('pv.deleted', 0);
		$this->db->where('v.deleted', 0);
		if (!empty($id)) {
			$this->db->where('pv.id_producto', $id);
		}
		$this->db->group_by('pv.id_producto', 0);

		$ventas = $this->db->get()->result_array();

		foreach($ventas as $v){
			$data['ultima_venta'] = $v['fecha_venta'];
			$this->db->where('id', $v['id_producto']);
			$this->db->update('productos', $data);
		}

		//Busca la ultima fecha de COMPRA de cada producto
		$this->db->select('cp.id_producto, MAX(fecha) as fecha');
		$this->db->from('compras_productos cp');
		$this->db->join('compras c', 'cp.id_compra = c.id', 'left');
		$this->db->where('cp.deleted', 0);
		$this->db->where('c.deleted', 0);
		if (!empty($id)) {
			$this->db->where('cp.id_producto', $id);
		}
		$this->db->group_by('cp.id_producto', 0);

		$compras = $this->db->get()->result_array();

		$data = [];

		foreach($compras as $c){
			$data['ultima_compra'] = $c['fecha'];
			$this->db->where('id', $c['id_producto']);
			$this->db->update('productos', $data);
		}
	}

	/* 
	 * Recalcula Stock, productos vendidos y productos disponibles
	 */
	public function ajusta_stock($id = null) {
		//busca todos los productos existentes
		$this->db->select('id, nombre, vendidos, stock, disponibles, reservados, total_vendido, comprados, mermados');
		$this->db->from('productos p');
		$this->db->where('p.deleted', 0);
		if (!empty($id)) {
			$this->db->where('p.id', $id);
		}
		$productos = $this->db->get()->result_array();

		$modificados = [];

		// Busca Ventas: Boletas y Facturas
		foreach ($productos as $p) {
			$this->db->select('SUM(CASE WHEN ev.vendido = 1 THEN cantidad ELSE 0 END) vendidos,
							SUM(CASE WHEN ev.vendido = 1 THEN precio_total ELSE 0 END) total_vendido,
							SUM(CASE WHEN ev.reservado = 1 THEN cantidad ELSE 0 END) reservados,
							SUM(CASE WHEN ev.enviado = 1 THEN cantidad ELSE 0 END) enviados');
			$this->db->from('productos_ventas pv');
			$this->db->join('ventas v','pv.id_venta = v.id');
			$this->db->join('estados_ventas ev','v.id_estado_venta = ev.id');
			$this->db->where('pv.id_producto', $p['id']);
			$this->db->where_in('v.id_tipo_venta', [1, 6]);
			$this->db->where('pv.deleted', 0);
			$this->db->where('v.deleted', 0);
			$ventas = $this->db->get()->row_array();

			// Dejar todo lo NULL en 0
			$ventas['vendidos'] = intval($ventas['vendidos']);
			$ventas['total_vendido'] = intval($ventas['total_vendido']);
			$ventas['reservados'] = intval($ventas['reservados']);
			$ventas['enviados'] = intval($ventas['enviados']);

			// Busca mermas y devoluciones
			$this->db->select('SUM(cantidad) cantidad');
			$this->db->from('productos_ventas pv');
			$this->db->join('ventas v','pv.id_venta = v.id');
			$this->db->where('pv.id_producto', $p['id']);
			$this->db->where_in('v.id_tipo_venta', [3, 4]);
			$this->db->where('pv.deleted', 0);
			$this->db->where('v.deleted', 0);
			$mermas = $this->db->get()->row_array();
			// Dejar en 0 si es NULL
			$mermas['cantidad'] = intval($mermas['cantidad']);

			// Busca compras
			$this->db->select('SUM(cantidad) comprados, SUM(costo_total) costo_total');
			$this->db->from('compras_productos cp');
			$this->db->join('compras c','cp.id_compra = c.id');
			$this->db->where('cp.id_producto', $p['id']);
			$this->db->where('cp.deleted', 0);
			$this->db->where('c.deleted', 0);
			$compras = $this->db->get()->row_array();

			//guarda en DB valores de vendidos y total venta
			$data['vendidos'] = $ventas['vendidos'];
			$data['stock'] = $compras['comprados'] - $ventas['enviados'] - $mermas['cantidad'];
			$data['disponibles'] = $data['stock'] - $ventas['reservados'];
			$data['reservados'] = $ventas['reservados'];
			$data['total_vendido'] = !empty($ventas['total_vendido']) ? $ventas['total_vendido'] : 0;
			$data['comprados'] = $compras['comprados'];
			$data['mermados'] = $mermas['cantidad'];

			if ($p['vendidos'] != $data['vendidos']
				|| $p['stock'] != $data['stock']
				|| $p['disponibles'] != $data['disponibles']
				|| $p['reservados'] != $data['reservados']
				|| $p['total_vendido'] != $data['total_vendido']
				|| $p['comprados'] != $data['comprados']
				|| $p['mermados'] != $data['mermados']
			) {
				$this->db->where('id', $p['id']);
				$this->db->update('productos', $data);

				$m = [];
				$m['Id'] = $p['id'];
				$m['Nombre'] = $p['nombre'];
				$m['Detalle'] = '<br>Stock: ' . $p['stock'] . ' => ' . $data['stock'] . '<br>' .
										'Disponibles: ' . $p['disponibles'] . ' => ' . $data['disponibles'] . '<br>' .
										'Reservados: ' . $p['reservados'] . ' => ' . $data['reservados'] . '<br>' .
										'Comprados: ' . $p['comprados'] . ' => ' . $data['comprados'] . '<br>' .
										'Vendidos: ' . $p['vendidos'] . ' => ' . $data['vendidos'] . '<br>' .
										'Mermados: ' . $p['mermados'] . ' => ' . $data['mermados'] . '<br>' .
										'Total $ Vendido: ' . clp($p['total_vendido']) . ' => ' . clp($data['total_vendido']) . '<br>';

				$modificados[] = $m;
			}
		}
		return $modificados;
	}

	/**
	 * Busca productos de PS y los relaciona a los productos que tengan el mismo nombre
	 */
	public function ps_relation() {
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps_products = $ps->get()->result_array();

		$count = 0;
		foreach($ps_products as $ps_product){
			$this->db->select('id, nombre');
			$this->db->from('productos');
			$this->db->where('nombre', $ps_product['name']);
			$this->db->where('deleted', 0);
			$this->db->where('ps_id', 0);
			$producto = $this->db->get()->row_array();
			if(!empty($producto)){
				//pr($producto);
				$ps_data['ps_id'] = $ps_product['id_product'];
				$this->db->where('id', $producto['id']);
				$this->db->update('productos', $ps_data);
				$count++;
			}
		}

		pr('Se relacionaron ' . $count . ' productos');
		die;
	}

	/**
	 * Descuenta stock de PrestaShop por venta presencial
	 */
	public function ps_descontar($idProducto, $cantidad, $idCanal) {
		$psId = $this->get_ps_id($idProducto);

		if ($psId > 0) {
			$ps = $this->load->database('prestashop', TRUE);

			$ps->select("id_stock_available, quantity, physical_quantity");
			$ps->from('ps_stock_available');
			$ps->where('id_product', $psId);
			$ps->where('id_shop', 1);

			$psStock = $ps->get()->row_array();

			if (!empty($psStock)) {
				$newStock['quantity'] = $psStock['quantity'] - $cantidad;
				$newStock['physical_quantity'] =  $psStock['physical_quantity'] - $cantidad;

				$ps->where('id_stock_available', $psStock['id_stock_available']);
				$ps->update('ps_stock_available', $newStock);

				$reasons = [
					2 => 13,
					3 => 14,
					4 => 15,
					5 => 16
				];
				$idReason = $reasons[$idCanal] ?? 13;

				$psMvt = [
					'id_stock' => $psStock['id_stock_available'],
					'id_stock_mvt_reason' => $idReason,
					'id_employee' => $_SESSION['usuario']['ps_id'],
					'employee_firstname' => $_SESSION['usuario']['alias'],
					'employee_lastname' => '',
					'physical_quantity' => $cantidad,
					'sign' => '-1',
					'date_add' => date('Y-m-d H:i:s')
				];

				$ps->insert('ps_stock_mvt', $psMvt);
			}
		}

		return true;
	}


	public function update_names($ps_id = 0) {
		// Busca todos los prodcutos que tengan ps_id
		$this->db->select('id, ps_id, nombre');
		$this->db->from('productos p');
		$this->db->where('p.deleted', 0);
		$this->db->where('p.ps_id !=', 0);
		$this->db->order_by('p.id', 'ASC');
		$productos = $this->db->get()->result_array();

		// Busca todos los productos de ps
		$ps = $this->load->database('prestashop', TRUE);

		$ps->select("p.id_product, l.name");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product');
		$ps->where('l.id_lang', 1);
		$ps_products = $ps->get()->result_array();

		foreach ($ps_products as $ps_product) {
			$psp[$ps_product['id_product']] = $ps_product['name'];
		}

		// por cada producto en VDM compara el nombre con PS
		foreach ($productos as $p) {
			if (!empty($psp[$p['ps_id']])) {
				// Si es distinto lo actualiza
				if ($p['nombre'] != $psp[$p['ps_id']]) {
					$np['nombre'] = $psp[$p['ps_id']];
					$a[$p['id']] = $p['nombre'] . ' => ' . $np['nombre'];

					$this->db->where('id', $p['id']);
					$this->db->update('productos', $np);
				}
			}
		}

		return $a;
	}


	/**
	 * Actualiza stock disponible en PS según stock disponible en almacenes
	 */
	public function ajusta_stock_ps($idProductos) {
		// Si $idProductos no es array lo convierte en array con 1 elemento
		if (!is_array($idProductos)) {
			$idProductos = [$idProductos];
		}
		
		$CI =& get_instance();
		$CI->load->model('almacen_model', 'Almacen');
		$almacenes = $this->Almacen->listado();

		$ps = $this->load->database('prestashop', TRUE);

		foreach ($idProductos as $idProducto) {
			// Busca stock disponible en almacenes
			$producto['almacenes'] = $this->stock_almacenes($idProducto);

			$newStockPS = 0;
			$reservados = 0;
			foreach($almacenes as $almacen) {
				if (!empty($almacen['stock_online'])) {
					$newStockPS += $producto['almacenes'][$almacen['id']]['disponibles'];
				}
				// Se revisa si hay reservados en los alamcenes que no suman al stock online
				if (empty($almacen['stock_online'])) {
					if ($producto['almacenes'][$almacen['id']]['disponibles'] < 0) {
						$reservados += $producto['almacenes'][$almacen['id']]['disponibles'];
					}
				}
			}
			// Si hay reservados en otros almacenes se descuenta del stock online
			$newStockPS = $newStockPS + $reservados;

			$psId = $this->get_ps_id($idProducto);

			if ($psId > 0) {
				$ps->select("sa.id_stock_available, sa.quantity, sa.physical_quantity, sa.reserved_quantity, pl.available_now");
				$ps->from('ps_stock_available sa');
				$ps->join('ps_product_lang pl','ON sa.id_product = pl.id_product', 'left');
				$ps->where('sa.id_product', $psId);
				$ps->where('sa.id_shop', 1);

				$psStock = $ps->get()->row_array();
				
				if (!empty($psStock)) {
					// SOLO ACTUALIZA SI PRODUCTO ESTÁ DISPONIBLE, NO PREVENTA
					if (trim($psStock['available_now']) == '') {
						if ($psStock['quantity'] != $newStockPS) {

							$newStock['quantity'] = $newStockPS;
							$newStock['physical_quantity'] =  $newStockPS + $psStock['reserved_quantity'];

							$ps->where('id_stock_available', $psStock['id_stock_available']);
							$ps->update('ps_stock_available', $newStock);

							if ($psStock['quantity'] < $newStockPS) {
								$idReason = 17;
								$sign = '1';
								$quantity = $newStockPS - $psStock['quantity'];
							}
							else {
								$idReason = 18;
								$sign = '-1';
								$quantity = $psStock['quantity'] - $newStockPS;
							}

							$psMvt = [
								'id_stock' => $psStock['id_stock_available'],
								'id_stock_mvt_reason' => $idReason,
								'id_employee' => $_SESSION['usuario']['ps_id'],
								'employee_firstname' => $_SESSION['usuario']['alias'],
								'employee_lastname' => '',
								'physical_quantity' => $quantity,
								'sign' => $sign,
								'date_add' => date('Y-m-d H:i:s')
							];

							$ps->insert('ps_stock_mvt', $psMvt);
						}
					}
				}
			}
		}
	}


	public function categorias_ps(){
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('c.id_category, cl.name, c.id_parent, c.active, c.position');
		$ps->from('ps_category c');
		$ps->join('ps_category_lang cl','ON c.id_category = cl.id_category', 'left');
		$ps->where('cl.id_shop', 1);
		$ps->where('c.id_category >', 1);
		$ps->order_by('c.id_parent ASC');
		$ps->order_by('c.position ASC');

		$categories = $ps->get()->result_array();

		$categorias = [];

		foreach ($categories as $c) {
			if ($c['id_parent'] == 1 || $c['id_parent'] == 2) {
				$categorias[$c['id_category']] = $c;
			}
			else {
				$categorias[$c['id_parent']]['subcategories'][$c['id_category']] = $c;
			}
		}

		return $categorias;
	}


	public function productos_ps($idCategoria){
		$ps = $this->load->database('prestashop', TRUE);
		$ps->select('c.id_category, cl.name, c.id_parent, c.active, c.position');
		$ps->from('ps_category c');
		$ps->join('ps_category_lang cl','ON c.id_category = cl.id_category', 'left');
		$ps->where('cl.id_shop', 1);
		$ps->where('c.id_category', $idCategoria);
		$category = $ps->get()->row_array();

		$ps->select("p.id_product, l.name, p.active, p.id_category_default, cp.position, sa.quantity");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product', 'left');
		$ps->join('ps_category_product cp','cp.id_product = p.id_product AND cp.id_category = ' . $idCategoria, 'left');
		$ps->join('ps_stock_available sa','ON p.id_product = sa.id_product', 'left');
		$ps->where('l.id_lang', 1);
		//$ps->where('p.active', 1);
		$ps->where('cp.id_category', $idCategoria);
		$ps->order_by('cp.position', 'ASC');
		$products = $ps->get()->result_array();

		$category['productos'] = $products;

		return $category;
	}


	public function agregar_categoria($post){
		$ps = $this->load->database('prestashop', TRUE);

		$ps->from('ps_category_product');
		$ps->where('id_product', $post['id_product']);
		$ps->where('id_category', $post['id_category']);
		$product = $ps->get()->row_array();

		if(!empty($product)) {
			return false;
		}

		$ps->select('max(position) as pos');
		$ps->from('ps_category_product');
		$ps->where('id_category', $post['id_category']);
		$pos = $ps->get()->row_array();

		// Crea Relación con categoría en PS
		$cp = [
			'id_category' => $post['id_category'],
			'id_product' => $post['id_product'],
			'position' => $pos['pos'] + 1
		];	
		$ps->insert('ps_category_product', $cp);


		$ps->select("p.id_product, l.name, p.active, p.id_category_default, cp.position, sa.quantity");
		$ps->from('ps_product p');
		$ps->join('ps_product_lang l','l.id_product = p.id_product', 'left');
		$ps->join('ps_category_product cp','cp.id_product = p.id_product AND cp.id_category = ' . $post['id_category'], 'left');
		$ps->join('ps_stock_available sa','ON p.id_product = sa.id_product', 'left');
		$ps->where('l.id_lang', 1);
		$ps->where('p.id_product', $post['id_product']);
		$ps->where('cp.id_category', $post['id_category']);
		$product = $ps->get()->row_array();

		return $product;
	}

	public function remover_categoria($post){
		$ps = $this->load->database('prestashop', TRUE);
		$ps->where('id_product', $post['id_product']);
		$ps->where('id_category', $post['id_category']);
		$ps->delete('ps_category_product');

		return true;
	}

	/**
	 *Actualiza categoria oferta en producto
	 */
	public function actualiza_oferta_ps($psId = 0) {
		$idOferta = 98;
		$ps = $this->load->database('prestashop', TRUE);

		// Busca si producto tiene stock
		$ps->select('quantity');
		$ps->from('ps_stock_available');
		$ps->where('id_product', $psId);
		$stock = $ps->get()->row_array();

		// Busca en PS Si producto tiene categoría oferta
		$ps->from('ps_category_product');
		$ps->where('id_product', $psId);
		$ps->where('id_category', $idOferta);
		$productCategory = $ps->get()->row_array();

		// Busca en PS si hay alguna regla asociada al producto que se encuentre activa
		$ps->from('ps_specific_price');
		$ps->where('id_product', $psId);
		$ps->where('id_shop', 1);
		$specificPrices = $ps->get()->result_array();

		$discounts = [];
		// Verificar si specific prices se encuentran activos
		foreach($specificPrices as $specificPrice) {
			$valid = true;
			if (isDate($specificPrice['from'])) {
				if (strtotime($specificPrice['from']) > time()) {
					// Aún no parte!
					$valid = false;
				}
			}

			if (isDate($specificPrice['to'])) {
				if (strtotime($specificPrice['to']) < time()) {
					// Ya acabó!
					$valid = false;
				}
			}

			if ($valid) {
				$discounts[] = $specificPrice;
			}
		}

		$data = [
			'id_product' => $psId,
			'id_category' => $idOferta
		];

		// Si no hay cambio se retorna false
		$result['status'] = false;

		// Si tiene categoría y no tiene descuento o no tiene stock
		if (!empty($productCategory) && (empty($discounts) || $stock['quantity'] < 1)) {
			// Eliminar categoría Oferta
			$this->remover_categoria($data);
			$result['status'] = 'removed';
		}
		else if (empty($productCategory) && !empty($discounts)) {
			// Agregar categoría Oferta
			$this->agregar_categoria($data);
			$result['status'] = 'added';
		}

		return $result;
	}

	/**
	 * Actualiza producto en Prestashop para quitar toda la info de Preventa y dejarlo Disponible 
	 * Quita categorias Preventa y Preventas en Oferta
	 * Agrega Categoría Recién LLegados
	 * Cambia Característica Disponibilidad a Inmediata
	 * Quita Característica Fecha de LLegada
	 * Quita Etiqueta Preventa
	 * Quita Fecha de Disponibilidad
	 */
	public function quita_preventa($psId) {
		$idCatPreventa = 21;
		$idCatPreventasOfertas = 92;
		$idSpecificPriceRule = 38;
		$idCatRecienLLegados = 110;
		$idCarDisponibilidad = 19;
		$idCarDisPreventa = 6579;
		$idCarDisInmediata = 6578;
		$idCarFechaLlegada = 25;

		// Carga DB Prestashop
		$ps = $this->load->database('prestashop', TRUE);

		// Quitar Categorías Preventa y Preventas en Oferta
		$ps->where('id_product', $psId);
		$ps->where_in('id_category', [$idCatPreventa, $idCatPreventasOfertas]);
		$ps->delete('ps_category_product');

		// Buscas si existe algún descuento asociado al producto por tener la categoría preventa en oferta
		$ps->where('id_product', $psId);
		$ps->where('id_specific_price_rule', $idSpecificPriceRule);
		$ps->delete('ps_specific_price');


		// Agregar categoria Recién Llegados
		//Busca si ya está agregado
		$ps->from('ps_category_product');
		$ps->where('id_category', $idCatRecienLLegados);
		$ps->where('id_product', $psId);
		$recienLlegado = $ps->get()->row_array();
		// Si NO existe se agrega
		if (empty($recienLlegado)) {
			// Consulta position max en PS
			$ps->select('max(position) as pos');
			$ps->from('ps_category_product');
			$ps->where('id_category', $idCatRecienLLegados);
			$pos = $ps->get()->row_array();
			// Agrega Relación con categoría en PS
			$cp = [
				'id_category' => $idCatRecienLLegados,
				'id_product' => $psId,
				'position' => $pos['pos'] + 1
			];	
			$ps->insert('ps_category_product', $cp);
		}


		// Elimina disponibilidad preventa
		$ps->where('id_product', $psId);
		$ps->where('id_feature_value', $idCarDisPreventa);
		$ps->delete('ps_feature_product');

		// Agrega disponibilidad Inmediata // Solo si no existe
		$ps->from('ps_feature_product');
		$ps->where('id_feature', $idCarDisponibilidad);
		$ps->where('id_product', $psId);
		$ps->where('id_feature_value', $idCarDisInmediata);
		$disInmediata = $ps->get()->row_array();
		// Si NO existe se agrega
		if (empty($disInmediata)) {
			$fp = [
				'id_feature' => $idCarDisponibilidad,
				'id_product' => $psId,
				'id_feature_value' => $idCarDisInmediata,
			];	
			$ps->insert('ps_feature_product', $fp);
		}

		// Elimina caracteristica Fecha de Llegada
		$ps->where('id_product', $psId);
		$ps->where('id_feature', $idCarFechaLlegada);
		$ps->delete('ps_feature_product');

		// Quita etiqueta preventa
		$pl = ['available_now' => ''];
		$ps->where('id_product', $psId);
		$ps->where('id_shop', 1);
		$ps->where('id_lang', 1);
		$ps->update('ps_product_lang', $pl);
		
		// Quita fecha de disponibilidad
		$pl = ['available_date' => '0000-00-00'];
		$ps->where('id_product', $psId);
		$ps->update('ps_product', $pl);

		$ps->where('id_product', $psId);
		$ps->where('id_shop', 1);
		$ps->update('ps_product_shop', $pl);

	}

	public function crear_almacen_producto($idProducto, $idAlmacen) {
		$this->db->select('id');
		$this->db->from('almacenes_productos');
		$this->db->where('id_producto', $idProducto);
		$this->db->where('id_almacen', $idAlmacen);
		$this->db->where('deleted', 0);
		$row = $this->db->get()->row_array();

		if ($row) {
			return $row['id'];
		}

		$data = [
			'id_producto'  => $idProducto,
			'id_almacen'   => $idAlmacen,
			'id_ubicacion' => 0,
			'stock'        => 0,
			'reservados'   => 0,
			'en_transito'  => 0,
			'disponibles'  => 0,
			'deseados'     => 0,
			'deseados_max' => 0,
			'deleted'      => 0,
		];

		$this->db->insert('almacenes_productos', $data);
		return $this->db->insert_id();
	}

	/*******************************************************************************************/
	/*                              Funciones Privadas                                         */
	/*******************************************************************************************/

	private function purchases_data($compras){
		//calcular total comprado
		//total gastado
		//promedio de compra
		//Fecha deben venir DESC para correcta alta
		$q = 0;
		$t = 0;
		$a = '';
		foreach($compras as $c) {
			$a = $c['fecha_recepcion'];
			$q += intval($c['cantidad']);
			$t += intval($c['costo_total']*1.19);
		}

		if ($q > 0) {
			$p = intval($t/$q);
		}
		else {
			$p = 0;
		}

		$purchases_data = [
			'comprados' => $q,
			'total' => $t,
			'promedio' => $p,
			'alta' => $a
		];
		return $purchases_data;
	}

	private function sales_data($ventas){
		//calcular total vendido
		//total ganado
		//promedio de venta
		$q = 0;
		$t = 0;
		foreach($ventas as $v) {
			$q += intval($v['cantidad']);
			$t += $v['precio_total'];
		}

		if ($q > 0) {
			$p = intval($t/$q);
		}
		else {
			$p = 0;
		}

		$purchases_data = [
			'vendidos' => $q,
			'total' => $t,
			'promedio' => $p
		];
		return $purchases_data;
	}

	public function crear_almacenes_productos($idProducto) {
		$this->db->select('id');
		$this->db->from('almacenes');
		$this->db->where('activo', 1);
		$almacenes = $this->db->get()->result_array();

		foreach ($almacenes as $a) {
			$this->crear_almacen_producto($idProducto, $a['id']);
		}
	}
}