<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Comprobante extends MY_Controller {

    public function __construct() {
        parent::__construct();
        $this->load->model('ventas/Comprobante_model');
        $this->load->model('ventas/Orden_pedido_model');
        $this->load->model('configuracion/Empresa_model');
        $this->load->model('configuracion/Banco_cuenta_model');
        $this->load->model('almacen/Guiaremitente_model');
        $this->load->model('almacen/ParteSalida_model');
        $this->load->helper('numero');

    }

    public function index() {
        $this->validar_permiso('comprobante_ver');

        $grupo = $this->input->get('grupo', TRUE);
        if ($grupo === null || $grupo === '') {
            $grupo = 'comprobantes'; // Por defecto: Facturas y Boletas
        }

        $data['titulo']        = 'Comprobantes';
        $data['comprobantes']  = $this->Comprobante_model->listar($grupo);
        $data['grupo']         = $grupo;

        $this->layout('ventas/comprobante/index', $data);
    }

    public function create() {
        $this->validar_permiso('comprobante_crear');

        $data['titulo'] = 'Nuevo Comprobante';
        $data['tipos_comprobante'] = $this->Comprobante_model->listar_tipos_comprobante_venta();
        $data['formas_pago'] = $this->Comprobante_model->listar_formas_pago();
        $data['tributo_igv'] = $this->Comprobante_model->obtener_tributo_igv();
        $data['almacenes'] = $this->ParteSalida_model->listar_almacenes();
        $this->layout('ventas/comprobante/create', $data);
    }

    public function store() {
        $this->validar_permiso('comprobante_crear');

        $this->form_validation->set_rules('comprobante_id', 'Tipo de comprobante', 'required');
        $this->form_validation->set_rules('cliente_id', 'Cliente', 'required');
        $this->form_validation->set_rules('fecha', 'Fecha', 'required');
        $this->form_validation->set_rules('fp_id', 'Forma de pago', 'required');

        if ($this->form_validation->run() == FALSE) {
            return $this->create();
        }

        $comprobante_id = (int)$this->input->post('comprobante_id', TRUE);
        $cliente_id = (int)$this->input->post('cliente_id', TRUE);
        $fp_id      = (int)$this->input->post('fp_id', TRUE);
        $almacen_id = (int)$this->input->post('almacen_id', TRUE);

        $detalle_producto_id        = $this->input->post('detalle_producto_id');
        $detalle_descripcion        = $this->input->post('detalle_descripcion');
        $detalle_cantidad           = $this->input->post('detalle_cantidad');
        $detalle_precio_unitario    = $this->input->post('detalle_precio_unitario');
        $detalle_subtotal           = $this->input->post('detalle_subtotal');

        if (empty($detalle_producto_id) || !is_array($detalle_producto_id)) {
            $this->session->set_flashdata('error', 'Debe agregar al menos un producto al detalle');
            redirect('ventas/comprobante/create');
        }

        $cliente = $this->Comprobante_model->obtener_cliente($cliente_id);
        if (!$cliente) {
            $this->session->set_flashdata('error', 'Cliente no válido');
            redirect('ventas/comprobante/create');
        }

        if (!$almacen_id) {
            $this->session->set_flashdata('error', 'Debe seleccionar un almacén');
            redirect('ventas/comprobante/create');
        }

        $tipoComprobante = $this->Comprobante_model->obtener_tipo_comprobante($comprobante_id);
        if (!$tipoComprobante) {
            $this->session->set_flashdata('error', 'Tipo de comprobante no válido');
            redirect('ventas/comprobante/create');
        }

        $tributo = $this->Comprobante_model->obtener_tributo_igv();
        if (!$tributo) {
            $this->session->set_flashdata('error', 'No se encontró la configuración del IGV en mt_tributo ID 2');
            redirect('ventas/comprobante/create');
        }

        $formaPago = $this->Comprobante_model->obtener_forma_pago($fp_id);
        if (!$formaPago) {
            $this->session->set_flashdata('error', 'Forma de pago no válida');
            redirect('ventas/comprobante/create');
        }

        $fecha = $this->input->post('fecha', TRUE);
        $fecha_db = date('Y-m-d H:i:s', strtotime($fecha));

        $modoIncluyeIgv = ((int)$tributo->modo === 1);
        $igvPorcentaje = (float)$tributo->valor_porcentual;

        $sumatoriaDetalle = 0;
        $detalleGuardar = array();
        $detraccionIds = array();

        for ($i = 0; $i < count($detalle_producto_id); $i++) {
            if (empty($detalle_producto_id[$i])) {
                continue;
            }

            $producto = $this->Comprobante_model->obtener_producto((int)$detalle_producto_id[$i]);
            if (!$producto) {
                continue;
            }

            $cantidad = isset($detalle_cantidad[$i]) ? (float)$detalle_cantidad[$i] : 0;
            $precioUnitario = isset($detalle_precio_unitario[$i]) ? (float)$detalle_precio_unitario[$i] : 0;
            $subtotalItem = isset($detalle_subtotal[$i]) ? (float)$detalle_subtotal[$i] : 0;

            if ($cantidad <= 0 || $precioUnitario < 0 || $subtotalItem <= 0) {
                continue;
            }

            $sumatoriaDetalle += $subtotalItem;

            $detalleGuardar[] = array(
                'producto_id' => $producto->id,
                'descripcion' => !empty($detalle_descripcion[$i]) ? trim($detalle_descripcion[$i]) : $producto->nombre,
                'cantidad' => $cantidad,
                'precio_unitario' => round($precioUnitario, 4),
                'subtotal' => round($subtotalItem, 2)
            );

            if (!empty($producto->detraccion_id) && (int)$producto->detraccion_id > 1) {
                $detraccionIds[] = (int)$producto->detraccion_id;
            }
        }

        if (empty($detalleGuardar)) {
            $this->session->set_flashdata('error', 'No hay detalle válido para guardar');
            redirect('ventas/comprobante/create');
        }

        $detraccionIds = array_unique($detraccionIds);

        if (count($detraccionIds) > 1) {
            $this->session->set_flashdata('error', 'No se puede generar un comprobante con productos de diferentes detracciones');
            redirect('ventas/comprobante/create');
        }

        // Totales base del comprobante
        if ($modoIncluyeIgv) {
            $total = round($sumatoriaDetalle, 2);
            $subtotal = round($total / (1 + ($igvPorcentaje / 100)), 2);
            $igv = round($total - $subtotal, 2);
        } else {
            $subtotal = round($sumatoriaDetalle, 2);
            $igv = round($subtotal * ($igvPorcentaje / 100), 2);
            $total = round($subtotal + $igv, 2);
        }

        // Iniciales
        $importeRetencion = 0;
        $importeDetraccion = 0;
        $retencion = null;
        $detraccion = null;
        $pagoNeto = $total;

        $esFactura = ((int)$tipoComprobante->id === 1);
        $esCredito = (strtoupper(trim($formaPago->codigo)) === 'CREDITO' || strtoupper(trim($formaPago->nombre)) === 'CREDITO');

        // 1) Detracción tiene prioridad
        if ($esFactura && count($detraccionIds) === 1) {
            $detraccion = $this->Comprobante_model->obtener_detraccion($detraccionIds[0]);

            if ($detraccion && (float)$detraccion->porcentaje > 0 && (float)$total >= (float)$detraccion->monto_minimo) {
                $importeDetraccion = round($total * ((float)$detraccion->porcentaje / 100), 2);
                $pagoNeto = round($total - $importeDetraccion, 2);
            }
        }

        // 2) Si no hubo detracción, evaluar retención
        if ($importeDetraccion <= 0 && $esFactura) {
            $retencion = $this->Comprobante_model->obtener_retencion_cliente($cliente_id);

            if ($retencion && (float)$total >= (float)$retencion->monto_minimo) {
                $importeRetencion = round($total * ((float)$retencion->porcentaje / 100), 2);
                $pagoNeto = round($total - $importeRetencion, 2);
            }
        }

        // Vencimiento por defecto
        $fecvencimiento = date('Y-m-d', strtotime($fecha));

        // Cuotas
        $cuotasGuardar = array();

        if ($esFactura && $esCredito) {
            $cuota_fecha = $this->input->post('cuota_fecha');
            $cuota_importe = $this->input->post('cuota_importe');

            if (empty($cuota_fecha) || empty($cuota_importe) || !is_array($cuota_fecha) || !is_array($cuota_importe)) {
                $this->session->set_flashdata('error', 'Debe ingresar al menos una cuota para factura al crédito');
                redirect('ventas/comprobante/create');
            }

            $sumaCuotas = 0;
            $nroCuota = 1;

            for ($i = 0; $i < count($cuota_fecha); $i++) {
                $vencto = !empty($cuota_fecha[$i]) ? $cuota_fecha[$i] : null;
                $importe = isset($cuota_importe[$i]) ? (float)$cuota_importe[$i] : 0;

                if (empty($vencto) || $importe <= 0) {
                    continue;
                }

                $sumaCuotas += $importe;

                $cuotasGuardar[] = array(
                    'nro_cuota' => $nroCuota,
                    'vencto_cuota' => $vencto,
                    'importe_cuota' => round($importe, 2)
                );

                $nroCuota++;
            }

            if (empty($cuotasGuardar)) {
                $this->session->set_flashdata('error', 'Debe ingresar cuotas válidas');
                redirect('ventas/comprobante/create');
            }

            if (round($sumaCuotas, 2) != round($pagoNeto, 2)) {
                $this->session->set_flashdata('error', 'La suma de cuotas debe ser igual al pago neto del comprobante');
                redirect('ventas/comprobante/create');
            }

            $ultimasFechas = array_column($cuotasGuardar, 'vencto_cuota');
            rsort($ultimasFechas);
            $fecvencimiento = $ultimasFechas[0];
        }

        $this->db->trans_begin();

        $tipoComprobanteActual = $this->Comprobante_model->obtener_tipo_comprobante_for_update($comprobante_id);

        if (!$tipoComprobanteActual) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo obtener el correlativo del comprobante');
            redirect('ventas/comprobante/create');
        }

        $serie = trim($tipoComprobanteActual->serie);
        $correlativoActual = (int)$tipoComprobanteActual->correlativo_actual;
        $numero = str_pad($correlativoActual, 8, '0', STR_PAD_LEFT);

        $cabecera = array(
            'comprobante_id'    => $tipoComprobanteActual->id,
            'serie'             => $serie,
            'numero'            => $numero,
            'cliente_id'        => $cliente->id,
            'fecha'             => $fecha_db,
            'ordcomp_cliente'   => trim($this->input->post('ordcomp_cliente', TRUE)),
            'tipocambio'        => (float)$this->input->post('tipocambio', TRUE),
            'almacen_id'        => $almacen_id,
            'subtotal'          => $subtotal,
            'anticipo'          => 0,
            'igv_porcentaje'    => $igvPorcentaje,
            'igv'               => $igv,
            'otrostributos'     => 0,
            'total'             => $total,
            'pago_neto'         => $pagoNeto,
            'observaciones'     => trim($this->input->post('observaciones', TRUE)),
            'fecvencimiento'    => $fecvencimiento,
            'origen'            => 'LIBRE',
            'fp_id'             => $formaPago->id,
            'moneda_id'         => $cliente->moneda_id,
            'vendedor_id'       => $cliente->vendedor_id,
            'estado_id'         => 23,
            'saldo'             => $pagoNeto,  // saldo inicial = pago_neto
            'estado_cobro'      => 0,          // PENDIENTE
            'created_by'        => $this->session->userdata('id')
        );

        $comprobId = $this->Comprobante_model->insertar($cabecera);

        if (!$comprobId) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo guardar la cabecera del comprobante');
            redirect('ventas/comprobante/create');
        }

        foreach ($detalleGuardar as $item) {
            $this->Comprobante_model->insertar_detalle(array(
                'comprob_id' => $comprobId,
                'producto_id' => $item['producto_id'],
                'descripcion' => $item['descripcion'],
                'cantidad' => $item['cantidad'],
                'precio_unitario' => $item['precio_unitario'],
                'subtotal' => $item['subtotal']
            ));
        }

        if (!empty($cuotasGuardar)) {
            foreach ($cuotasGuardar as $cuota) {
                $this->Comprobante_model->insertar_cuota(array(
                    'comprob_id' => $comprobId,
                    'nro_cuota' => $cuota['nro_cuota'],
                    'vencto_cuota' => $cuota['vencto_cuota'],
                    'importe_cuota' => $cuota['importe_cuota']
                ));
            }
        }

        if ($importeRetencion > 0 && $retencion) {
            $this->Comprobante_model->insertar_retencion(array(
                'comprob_id' => $comprobId,
                'total' => $total,
                'porcentaje' => (float)$retencion->porcentaje,
                'importe_retencion' => $importeRetencion
            ));
        }

        if ($importeDetraccion > 0 && $detraccion) {
            $this->Comprobante_model->insertar_detraccion(array(
                'comprob_id' => $comprobId,
                'detraccion_id' => $detraccion->id,
                'total' => $total,
                'porcentaje' => (float)$detraccion->porcentaje,
                'importe_detraccion' => $importeDetraccion
            ));
        }

        $this->Comprobante_model->insertar_estado(array(
            'comprob_id' => $comprobId,
            'estado_id' => 23,
            'comprob_fechaestado' => date('Y-m-d H:i:s'),
            'usuario_id' => $this->session->userdata('id')
        ));

        $this->Comprobante_model->actualizar_correlativo_tipo_comprobante(
            $tipoComprobanteActual->id,
            $correlativoActual + 1
        );

        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo registrar el comprobante');
            redirect('ventas/comprobante/create');
        }

        // Crear parte de salida automáticamente
        $this->ParteSalida_model->crear_desde_comprobante(
            $comprobId,
            $almacen_id,
            $cliente_id,
            $fecha_db,
            $this->session->userdata('id')
        );

        $this->db->trans_commit();

        $this->session->set_flashdata('success', 'Comprobante registrado correctamente: ' . $serie . '-' . $numero);
        redirect('ventas/comprobante');
    }

    public function store_desde_orden($orp_id) {
        $this->validar_permiso('comprobante_crear');

        $orden = $this->Orden_pedido_model->obtener_para_facturacion($orp_id);

        if (!$orden) {
            show_404();
        }

        $this->form_validation->set_rules('comprobante_id', 'Tipo de comprobante', 'required');
        $this->form_validation->set_rules('fecha', 'Fecha', 'required');
        $this->form_validation->set_rules('fp_id', 'Forma de pago', 'required');

        if ($this->form_validation->run() == FALSE) {
            return $this->create_desde_orden($orp_id);
        }

        $comprobante_id = (int)$this->input->post('comprobante_id', TRUE);
        $cliente_id     = (int)$orden->cliente_id;
        $fp_id          = (int)$this->input->post('fp_id', TRUE);

        $almacen_id_orden = (int)$this->input->post('almacen_id', TRUE);
        if (!$almacen_id_orden) {
            $this->session->set_flashdata('error', 'Debe seleccionar un almacén');
            redirect('ventas/comprobante/create_desde_orden/' . $orp_id);
        }

        $detalle_orpd_id            = $this->input->post('detalle_orpd_id');
        $detalle_producto_id        = $this->input->post('detalle_producto_id');
        $detalle_descripcion        = $this->input->post('detalle_descripcion');
        $detalle_cantidad           = $this->input->post('detalle_cantidad');
        $detalle_precio_unitario    = $this->input->post('detalle_precio_unitario');
        $detalle_subtotal           = $this->input->post('detalle_subtotal');

        if (empty($detalle_producto_id) || !is_array($detalle_producto_id)) {
            $this->session->set_flashdata('error', 'Debe agregar al menos un producto al detalle');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        $cliente            = $this->Comprobante_model->obtener_cliente($cliente_id);
        $tipoComprobante    = $this->Comprobante_model->obtener_tipo_comprobante($comprobante_id);
        $tributo            = $this->Comprobante_model->obtener_tributo_igv();
        $formaPago          = $this->Comprobante_model->obtener_forma_pago($fp_id);

        if (!$cliente || !$tipoComprobante || !$tributo || !$formaPago) {
            $this->session->set_flashdata('error', 'Datos inválidos para generar comprobante');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        $fecha = $this->input->post('fecha', TRUE);
        $fecha_db = date('Y-m-d H:i:s', strtotime($fecha));

        $modoIncluyeIgv = ((int)$tributo->modo === 1);
        $igvPorcentaje = (float)$tributo->valor_porcentual;

        $sumatoriaDetalle = 0;
        $detalleGuardar = array();
        $detraccionIds = array();

        for ($i = 0; $i < count($detalle_producto_id); $i++) {
            if (empty($detalle_producto_id[$i])) {
                continue;
            }

            $producto = $this->Comprobante_model->obtener_producto((int)$detalle_producto_id[$i]);
            if (!$producto) {
                continue;
            }

            $orpdId = isset($detalle_orpd_id[$i]) ? (int)$detalle_orpd_id[$i] : 0;
            $detalleOrden = $this->db->where('id', $orpdId)->get('vt_orden_pedido_detalle')->row();

            if (!$detalleOrden) {
                continue;
            }

            $cantidad = isset($detalle_cantidad[$i]) ? (float)$detalle_cantidad[$i] : 0;
            $precioUnitario = isset($detalle_precio_unitario[$i]) ? (float)$detalle_precio_unitario[$i] : 0;
            $subtotalItem = isset($detalle_subtotal[$i]) ? (float)$detalle_subtotal[$i] : 0;

            if ($cantidad <= 0 || $precioUnitario < 0 || $subtotalItem <= 0) {
                continue;
            }

            $disponible = (float)$detalleOrden->orpd_saldo;

            if ($cantidad > $disponible) {
                $this->session->set_flashdata('error', 'La cantidad a facturar no puede superar el saldo disponible del ítem');
                redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
            }

            $sumatoriaDetalle += $subtotalItem;

            $detalleGuardar[] = array(
                'orpd_id'         => $orpdId,
                'producto_id'     => $producto->id,
                'descripcion'     => !empty($detalle_descripcion[$i]) ? trim($detalle_descripcion[$i]) : $producto->nombre,
                'cantidad'        => $cantidad,
                'precio_unitario' => round($precioUnitario, 4),
                'subtotal'        => round($subtotalItem, 2)
            );

            if (!empty($producto->detraccion_id) && (int)$producto->detraccion_id > 1) {
                $detraccionIds[] = (int)$producto->detraccion_id;
            }
        }

        if (empty($detalleGuardar)) {
            $this->session->set_flashdata('error', 'No hay detalle válido para guardar');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        $detraccionIds = array_unique($detraccionIds);

        if (count($detraccionIds) > 1) {
            $this->session->set_flashdata('error', 'No se puede generar un comprobante con productos de diferentes detracciones');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        if ($modoIncluyeIgv) {
            $total      = round($sumatoriaDetalle, 2);
            $subtotal   = round($total / (1 + ($igvPorcentaje / 100)), 2);
            $igv        = round($total - $subtotal, 2);
        } else {
            $subtotal   = round($sumatoriaDetalle, 2);
            $igv        = round($subtotal * ($igvPorcentaje / 100), 2);
            $total      = round($subtotal + $igv, 2);
        }


        $totalAnticipos = $this->Comprobante_model->obtener_total_anticipos_orden($orp_id);
        $anticipoAplicado = round((float)$totalAnticipos, 2);

        if ($anticipoAplicado > $total) {
            $anticipoAplicado = $total;
        }

        $saldoBase = round($total - $anticipoAplicado, 2);
        if ($saldoBase < 0) {
            $saldoBase = 0;
        }

        $importeRetencion = 0;
        $importeDetraccion = 0;
        $retencion = null;
        $detraccion = null;
        $pagoNeto = $saldoBase;

        $esFactura = ((int)$tipoComprobante->id === 1);
        $esCredito = (strtoupper(trim($formaPago->codigo)) === 'CREDITO' || strtoupper(trim($formaPago->nombre)) === 'CREDITO');

        if ($esFactura && count($detraccionIds) === 1) {
            $detraccion = $this->Comprobante_model->obtener_detraccion($detraccionIds[0]);

            if ($detraccion && (float)$detraccion->porcentaje > 0 && (float)$saldoBase >= (float)$detraccion->monto_minimo) {
                $importeDetraccion = round($saldoBase * ((float)$detraccion->porcentaje / 100), 2);
                $pagoNeto = round($saldoBase - $importeDetraccion, 2);
            }
        }

        if ($importeDetraccion <= 0 && $esFactura) {
            $retencion = $this->Comprobante_model->obtener_retencion_cliente($cliente_id);

            if ($retencion && (float)$saldoBase >= (float)$retencion->monto_minimo) {
                $importeRetencion = round($saldoBase * ((float)$retencion->porcentaje / 100), 2);
                $pagoNeto = round($saldoBase - $importeRetencion, 2);
            }
        }

        $fecvencimiento = date('Y-m-d', strtotime($fecha));
        $cuotasGuardar = array();

        if ($esFactura && $esCredito) {
            $cuota_fecha = $this->input->post('cuota_fecha');
            $cuota_importe = $this->input->post('cuota_importe');

            if (empty($cuota_fecha) || empty($cuota_importe) || !is_array($cuota_fecha) || !is_array($cuota_importe)) {
                $this->session->set_flashdata('error', 'Debe ingresar al menos una cuota para factura al crédito');
                redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
            }

            $sumaCuotas = 0;
            $nroCuota = 1;

            for ($i = 0; $i < count($cuota_fecha); $i++) {
                $vencto = !empty($cuota_fecha[$i]) ? $cuota_fecha[$i] : null;
                $importe = isset($cuota_importe[$i]) ? (float)$cuota_importe[$i] : 0;

                if (empty($vencto) || $importe <= 0) {
                    continue;
                }

                $sumaCuotas += $importe;

                $cuotasGuardar[] = array(
                    'nro_cuota'     => $nroCuota,
                    'vencto_cuota'  => $vencto,
                    'importe_cuota' => round($importe, 2)
                );

                $nroCuota++;
            }

            if (empty($cuotasGuardar)) {
                $this->session->set_flashdata('error', 'Debe ingresar cuotas válidas');
                redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
            }

            if (round($sumaCuotas, 2) != round($pagoNeto, 2)) {
                $this->session->set_flashdata('error', 'La suma de cuotas debe ser igual al pago neto del comprobante');
                redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
            }

            $ultimasFechas = array_column($cuotasGuardar, 'vencto_cuota');
            rsort($ultimasFechas);
            $fecvencimiento = $ultimasFechas[0];
        }

        $this->db->trans_begin();

        $tipoComprobanteActual = $this->Comprobante_model->obtener_tipo_comprobante_for_update($comprobante_id);

        if (!$tipoComprobanteActual) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo obtener el correlativo del comprobante');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        $serie = trim($tipoComprobanteActual->serie);
        $correlativoActual = (int)$tipoComprobanteActual->correlativo_actual;
        $numero = str_pad($correlativoActual, 8, '0', STR_PAD_LEFT);

        $cabecera = array(
            'comprobante_id'    => $tipoComprobanteActual->id,
            'serie'             => $serie,
            'numero'            => $numero,
            'cliente_id'        => $cliente->id,
            'fecha'             => $fecha_db,
            'ordcomp_cliente'   => !empty($orden->orp_ordcomp_cliente) ? $orden->orp_ordcomp_cliente : trim($this->input->post('ordcomp_cliente', TRUE)),
            'tipocambio'        => (float)$this->input->post('tipocambio', TRUE),
            'almacen_id'        => $almacen_id_orden,
            'subtotal'          => $subtotal,
            'anticipo'          => $anticipoAplicado,
            'igv_porcentaje'    => $igvPorcentaje,
            'igv'               => $igv,
            'otrostributos'     => 0,
            'total'             => $total,
            'pago_neto'         => $pagoNeto,
            'observaciones'     => trim($this->input->post('observaciones', TRUE)),
            'fecvencimiento'    => $fecvencimiento,
            'origen'            => 'ORDEN',
            'fp_id'             => $formaPago->id,
            'moneda_id'         => $cliente->moneda_id,
            'vendedor_id'       => $cliente->vendedor_id,
            'estado_id'         => 23,
            'saldo'             => $pagoNeto,  // saldo inicial = pago_neto
            'estado_cobro'      => 0,          // PENDIENTE
            'created_by'        => $this->session->userdata('id')
        );

        $comprobId = $this->Comprobante_model->insertar($cabecera);

        if (!$comprobId) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo guardar la cabecera del comprobante');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        foreach ($detalleGuardar as $item) {
            $this->Comprobante_model->insertar_detalle(array(
                'comprob_id'      => $comprobId,
                'producto_id'     => $item['producto_id'],
                'descripcion'     => $item['descripcion'],
                'cantidad'        => $item['cantidad'],
                'precio_unitario' => $item['precio_unitario'],
                'subtotal'        => $item['subtotal']
            ));

             if (!empty($item['orpd_id'])) {
                $this->Comprobante_model->insertar_relacion_orden_detalle(array(
                    'comprob_id'         => $comprobId,
                    'orp_id'             => $orp_id,
                    'orpd_id'            => $item['orpd_id'],
                    'cantidad_facturada' => $item['cantidad'],
                    'created_by'         => $this->session->userdata('id')
                ));
            }
        }

        if (!empty($cuotasGuardar)) {
            foreach ($cuotasGuardar as $cuota) {
                $this->Comprobante_model->insertar_cuota(array(
                    'comprob_id'    => $comprobId,
                    'nro_cuota'     => $cuota['nro_cuota'],
                    'vencto_cuota'  => $cuota['vencto_cuota'],
                    'importe_cuota' => $cuota['importe_cuota']
                ));
            }
        }

        if ($importeRetencion > 0 && $retencion) {
            $this->Comprobante_model->insertar_retencion(array(
                'comprob_id'        => $comprobId,
                'total'             => $total,
                'porcentaje'        => (float)$retencion->porcentaje,
                'importe_retencion' => $importeRetencion
            ));
        }

        if ($importeDetraccion > 0 && $detraccion) {
            $this->Comprobante_model->insertar_detraccion(array(
                'comprob_id'        => $comprobId,
                'detraccion_id'     => $detraccion->id,
                'total'             => $total,
                'porcentaje'        => (float)$detraccion->porcentaje,
                'importe_detraccion' => $importeDetraccion
            ));
        }

        if (!empty($item['orpd_id'])) {

            $this->Comprobante_model->insertar_relacion_orden(array(
                'comprob_id'     => $comprobId,
                'orp_id'         => $orp_id,
                'tipo_relacion'  => 'FINAL',
                'monto_aplicado' => $total,
                'estado'         => 1,
                'created_by'     => $this->session->userdata('id')
            ));
        }

        $this->Comprobante_model->insertar_estado(array(
            'comprob_id'            => $comprobId,
            'estado_id'             => 23,
            'comprob_fechaestado'   => date('Y-m-d H:i:s'),
            'usuario_id'            => $this->session->userdata('id')
        ));

        $this->Comprobante_model->actualizar_correlativo_tipo_comprobante(
            $tipoComprobanteActual->id,
            $correlativoActual + 1
        );

        $detallesOrden = $this->Orden_pedido_model->obtener_detalles_con_saldo($orp_id);

        foreach ($detallesOrden as $det) {
            $facturado = $this->Comprobante_model->obtener_total_facturado_detalle_orden($det->id);
            $nuevoSaldo = round((float)$det->orpd_cantidad - (float)$facturado, 2);

            if ($nuevoSaldo < 0) {
                $nuevoSaldo = 0;
            }

            $this->Orden_pedido_model->actualizar_saldo_detalle($det->id, $nuevoSaldo);
        }

        $this->Orden_pedido_model->recalcular_estado_orden($orp_id);

        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo registrar el comprobante');
            redirect('ventas/comprobante/create_desde_orden/'.$orp_id);
        }

        $this->ParteSalida_model->crear_desde_comprobante(
            $comprobId,
            $almacen_id_orden,
            $cliente_id,
            $fecha_db,
            $this->session->userdata('id')
        );

        $this->db->trans_commit();

        $this->session->set_flashdata('success', 'Comprobante generado correctamente desde la orden: ' . $serie . '-' . $numero);
        redirect('ventas/comprobante/ver/'.$comprobId);
    }

    public function store_desde_guia() {
        $this->validar_permiso('comprobante_crear');

        $guiasIds = $this->input->post('guias_ids');

        if (empty($guiasIds) || !is_array($guiasIds)) {
            $this->session->set_flashdata('error', 'Debe seleccionar al menos una guía');
            redirect('almacen/guiaremitente');
        }

        $guiasIds = array_map('intval', $guiasIds);

        $guias = $this->Guiaremitente_model->obtener_para_comprobante($guiasIds);

        if (empty($guias)) {
            $this->session->set_flashdata('error', 'No se encontraron guías válidas');
            redirect('almacen/guiaremitente');
        }

        $cliente_id = (int)$guias[0]->cliente_id;
        $orp_id     = (int)$guias[0]->orp_id;
        $moneda_id  = (int)$guias[0]->moneda_id;
        
        foreach ($guias as $g) {
            if ((int)$g->estado_id !== 29) {
                $this->session->set_flashdata('error', 'Solo se pueden facturar guías en estado EMITIDO');
                redirect('almacen/guiaremitente');
            }

            if ((int)$g->cliente_id !== $cliente_id || (int)$g->orp_id !== $orp_id) {
                $this->session->set_flashdata('error', 'Las guías deben pertenecer al mismo cliente y orden');
                redirect('almacen/guiaremitente');
            }
        }

        // ================= VALIDACIONES =================
        $this->form_validation->set_rules('comprobante_id', 'Tipo de comprobante', 'required');
        $this->form_validation->set_rules('fecha', 'Fecha', 'required');
        $this->form_validation->set_rules('fp_id', 'Forma de pago', 'required');
        $this->form_validation->set_rules('almacen_id', 'Almacén', 'required');
        if ($this->form_validation->run() == FALSE) {
            redirect('almacen/guiaremitente');
        }

        // ================= DETALLE =================
        $detalle_producto_id     = $this->input->post('detalle_producto_id');
        $detalle_descripcion     = $this->input->post('detalle_descripcion');
        $detalle_cantidad        = $this->input->post('detalle_cantidad');
        $detalle_precio          = $this->input->post('detalle_precio_unitario');
        $detalle_subtotal        = $this->input->post('detalle_subtotal');

        if (empty($detalle_producto_id)) {
            $this->session->set_flashdata('error', 'No hay detalle válido');
            redirect('almacen/guiaremitente');
        }
        $almacen_id_guia = (int)$this->input->post('almacen_id', TRUE);
        $detalleGuardar = array();
        $total = 0;

        for ($i = 0; $i < count($detalle_producto_id); $i++) {

            if (empty($detalle_producto_id[$i])) continue;

            $cantidad   = (float)$detalle_cantidad[$i];
            $precio     = (float)$detalle_precio[$i];
            $subtotal   = (float)$detalle_subtotal[$i];

            if ($cantidad <= 0 || $precio < 0) continue;

            $detalleGuardar[] = array(
                'producto_id'       => (int)$detalle_producto_id[$i],
                'descripcion'       => $detalle_descripcion[$i],
                'cantidad'          => $cantidad,
                'precio_unitario'   => $precio,
                'subtotal'          => $subtotal
            );

            $total += $subtotal;
        }

        if (empty($detalleGuardar)) {
            $this->session->set_flashdata('error', 'Detalle inválido');
            redirect('almacen/guiaremitente');
        }

        // ================= INICIO TRANSACCIÓN =================
        $this->db->trans_begin();

        // ================= SERIE =================
        $tipo = $this->Comprobante_model->obtener_tipo_comprobante($this->input->post('comprobante_id'));

        if (!$tipo) {
            $this->session->set_flashdata('error', 'No existe configuración de comprobante');
            redirect('almacen/guiaremitente');
        }

        $serie          = trim($tipo->serie);
        $correlativo    = (int)$tipo->correlativo_actual;
        $numero         = str_pad($correlativo, 8, '0', STR_PAD_LEFT);
        $fecha_db       = date('Y-m-d H:i:s', strtotime($fecha));
        // ================= IGV =================
        $igv        = $this->Comprobante_model->obtener_tributo_igv();
        $porcIgv    = (float)$igv->valor_porcentual / 100;

        $subtotal   = round($total / (1 + $porcIgv), 2);
        $montoIgv   = round($total - $subtotal, 2);

        // ================= ANTICIPOS =================
        $anticipo   = (float)$this->Comprobante_model->obtener_total_anticipos_orden($orp_id);

        $pagoNeto   = $total - $anticipo;

        // ================= CABECERA =================
        $cabecera = array(
            'comprobante_id'  => (int)$this->input->post('comprobante_id'),
            'serie'           => $serie,
            'numero'          => $numero,
            'cliente_id'      => $cliente_id,
            'fecha'           => date('Y-m-d H:i:s', strtotime($this->input->post('fecha'))),
            'almacen_id'      => $almacen_id_guia,
            'subtotal'        => $subtotal,
            'igv'             => $montoIgv,
            'total'           => $total,
            'anticipo'        => $anticipo,
            'pago_neto'       => $pagoNeto,
            'fp_id'           => (int)$this->input->post('fp_id'),
            'moneda_id'       => $moneda_id,
            'origen'          => $orp_id,
            'observaciones'   => $this->input->post('observaciones'),
            'estado_id'       => 1,
            'saldo'           => round($total - $anticipo, 2),
            'estado_cobro'    => 0,
            'created_by'      => $this->session->userdata('id')
        );

        $comprobId = $this->Comprobante_model->insertar($cabecera);

        if (!$comprobId) {
            $this->db->trans_rollback();
            redirect('almacen/guiaremitente');
        }

        // ================= DETALLE =================
        foreach ($detalleGuardar as $item) {
            $this->Comprobante_model->insertar_detalle(array(
                'comprob_id'        => $comprobId,
                'producto_id'       => $item['producto_id'],
                'descripcion'       => $item['descripcion'],
                'cantidad'          => $item['cantidad'],
                'precio_unitario'   => $item['precio_unitario'],
                'subtotal'          => $item['subtotal']
            ));
        }

        // ================= RELACIÓN GUÍA =================
        foreach ($guiasIds as $grId) {

            $this->Guiaremitente_model->insertar_comprobante_relacion(array(
                'gr_remitente_id' => $grId,
                'nrofactbolt_id'  => $comprobId
            ));

            $this->Guiaremitente_model->actualizar_estado($grId, 30);

            $this->Guiaremitente_model->insertar_estado(array(
                'gr_remitente_id' => $grId,
                'estado_id'       => 30,
                'gr_remitente_fechaestado' => date('Y-m-d H:i:s'),
                'usuario_id'      => $this->session->userdata('id')
            ));
        }

        // ================= CORRELATIVO =================
        $this->Comprobante_model->actualizar_correlativo_tipo_comprobante($tipo->id, $correlativo + 1);

        // ================= ESTADO ORDEN =================
        $this->Orden_pedido_model->recalcular_estado_orden($orp_id);

        // ================= FIN =================
        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'Error al generar comprobante');
            redirect('almacen/guiaremitente');
        }

        $this->ParteSalida_model->crear_desde_comprobante(
            $comprobId,
            $almacen_id_guia,
            $cliente_id,
            $fecha_db,
            $this->session->userdata('id')
        );

        $this->db->trans_commit();

        $this->session->set_flashdata('success', 'Comprobante generado: '.$serie.'-'.$numero);
        redirect('ventas/comprobante/ver/'.$comprobId);
    }

    public function buscar_clientes() {
        $term = trim($this->input->get('term', TRUE));

        if (strlen($term) < 2) {
            echo json_encode(array());
            return;
        }

        $clientes = $this->Comprobante_model->buscar_clientes($term);
        $resultado = array();

        foreach ($clientes as $item) {
            $resultado[] = array(
                'id'                => $item->id,
                'label'             => $item->nro_documento . ' - ' . $item->razon_social,
                'value'             => $item->nro_documento . ' - ' . $item->razon_social,
                'razon_social'      => $item->razon_social,
                'nro_documento'     => $item->nro_documento,
                'domicilio_fiscal'  => $item->domicilio_fiscal,
                'moneda_id'         => $item->moneda_id,
                'moneda_nombre'     => $item->moneda_nombre,
                'vendedor_id'       => $item->vendedor_id,
                'vendedor_nombre'   => $item->vendedor_nombre,
                'retencion_porcentaje' => isset($item->retencion_porcentaje) ? (float)$item->retencion_porcentaje : 0,
                'retencion_monto_minimo' => isset($item->retencion_monto_minimo) ? (float)$item->retencion_monto_minimo : 0,
                'tiene_retencion' => (!empty($item->retencion_porcentaje) && !empty($item->retencion_monto_minimo)) ? 1 : 0
            );
        }

        echo json_encode($resultado);
    }

    public function buscar_productos() {
        $term = trim($this->input->get('term', TRUE));

        if (strlen($term) < 2) {
            echo json_encode(array());
            return;
        }

        $productos = $this->Comprobante_model->buscar_productos($term);
        $resultado = array();

        foreach ($productos as $item) {
            $resultado[] = array(
                'id'                    => $item->id,
                'label'                 => $item->codigo . ' - ' . $item->nombre,
                'value'                 => $item->codigo . ' - ' . $item->nombre,
                'codigo'                => $item->codigo,
                'nombre'                => $item->nombre,
                'descripcion'           => $item->descripcion,
                'um_abreviado'          => $item->um_abreviado,
                'stock'                 => (float)$item->stock,
                'precio_unidad'         => (float)$item->precio_unidad,
                'precio_doce'           => (float)$item->precio_doce,
                'precio_veinticinco'    => (float)$item->precio_veinticinco,
                'precio_cincuenta'      => (float)$item->precio_cincuenta,
                'precio_cien'           => (float)$item->precio_cien,
                'precio_millar'         => (float)$item->precio_millar,
                'detraccion_id'         => (int)$item->detraccion_id,
                'detraccion_codigo'     => $item->detraccion_codigo,
                'detraccion_nombre'     => $item->detraccion_nombre,
                'detraccion_porcentaje' => isset($item->detraccion_porcentaje) ? (float)$item->detraccion_porcentaje : 0,
                'detraccion_monto_minimo' => isset($item->detraccion_monto_minimo) ? (float)$item->detraccion_monto_minimo : 0
                            );
        }

        echo json_encode($resultado);
    }

    public function obtener_precio_por_cantidad() {
        $producto_id = (int)$this->input->get('producto_id', TRUE);
        $cantidad = (float)$this->input->get('cantidad', TRUE);

        if (empty($producto_id) || $cantidad <= 0) {
            echo json_encode(array(
                'status' => false,
                'precio' => 0
            ));
            return;
        }

        $producto = $this->Comprobante_model->obtener_producto($producto_id);

        if (!$producto) {
            echo json_encode(array(
                'status' => false,
                'precio' => 0
            ));
            return;
        }

        $precio = $this->Comprobante_model->calcular_precio_por_cantidad($producto, $cantidad);

        echo json_encode(array(
            'status' => true,
            'precio' => round($precio, 4)
        ));
    }

    public function ver($id) {
        $this->validar_permiso('comprobante_ver');

        $comprobante = $this->Comprobante_model->obtener_completo($id);

        if (!$comprobante) {
            show_404();
        }

        $detalle    = $this->Comprobante_model->obtener_detalle($id);
        $cuotas     = $this->Comprobante_model->obtener_cuotas($id);
        $retencion  = $this->Comprobante_model->obtener_retencion_por_comprobante($id);
        $detraccion = $this->Comprobante_model->obtener_detraccion_por_comprobante($id);
        $historial  = $this->Comprobante_model->obtener_historial_estados($id);

        $data['titulo']         = 'Ver Comprobante';
        $data['comprobante']    = $comprobante;
        $data['detalle']        = $detalle;
        $data['cuotas']         = $cuotas;
        $data['retencion']      = $retencion;
        $data['detraccion']     = $detraccion;
        $data['historial']      = $historial;

        $this->layout('ventas/comprobante/ver', $data);
    }

    public function print($id) {
        $this->validar_permiso('comprobante_ver');
        $this->load->helper('numero');
        $comprobante = $this->Comprobante_model->obtener_completo($id);

        if (!$comprobante) {
            show_404();
        }

        $detalle = $this->Comprobante_model->obtener_detalle($id);
        $cuotas = $this->Comprobante_model->obtener_cuotas($id);
        $retencion = $this->Comprobante_model->obtener_retencion_por_comprobante($id);
        $detraccion = $this->Comprobante_model->obtener_detraccion_por_comprobante($id);
        $empresa = $this->Empresa_model->obtener_activa();
        $cuentas = $this->Banco_cuenta_model->listar_activas_para_impresion();

        $data['comprobante'] = $comprobante;
        $data['detalle'] = $detalle;
        $data['cuotas'] = $cuotas;
        $data['retencion'] = $retencion;
        $data['detraccion'] = $detraccion;
        $data['empresa'] = $empresa;
        $data['cuentas'] = $cuentas;


        $this->load->helper('file');
        require_once(APPPATH . 'libraries/phpqrcode/qrlib.php');

        $qrDir = FCPATH . 'uploads/qr/';
        if (!is_dir($qrDir)) {
            mkdir($qrDir, 0777, true);
        }

        $qrTexto = implode('|', array(
            !empty($empresa->ruc) ? $empresa->ruc : '',
            !empty($comprobante->tipo_comprobante_codigo) ? $comprobante->tipo_comprobante_codigo : '',
            $comprobante->serie,
            $comprobante->numero,
            number_format((float)$comprobante->igv, 2, '.', ''),
            number_format((float)$comprobante->total, 2, '.', ''),
            !empty($comprobante->fecha) ? date('Y-m-d', strtotime($comprobante->fecha)) : '',
            !empty($comprobante->cliente_nro_documento) ? $comprobante->cliente_nro_documento : ''
        ));

        $qrFileName = 'comprob_' . $comprobante->id . '.png';
        $qrFilePath = $qrDir . $qrFileName;

        QRcode::png($qrTexto, $qrFilePath, QR_ECLEVEL_L, 4, 1);

        $data['qr_texto'] = $qrTexto;
        $data['qr_url'] = base_url('uploads/qr/' . $qrFileName);


        $this->load->view('ventas/comprobante/print', $data);
    }

    public function create_desde_orden($orp_id) {
        $this->validar_permiso('comprobante_crear');

        $orden = $this->Orden_pedido_model->obtener_para_facturacion($orp_id);

        if (!$orden) {
            show_404();
        }

        $detalle = $this->Orden_pedido_model->obtener_detalle_para_facturacion($orp_id);

        if (empty($detalle)) {
            $this->session->set_flashdata('error', 'La orden no tiene detalle para facturar');
            redirect('ventas/orden-pedido');
        }

        $retencionCliente   = $this->Comprobante_model->obtener_retencion_cliente($orden->cliente_id);
        $totalAnticipos     = $this->Comprobante_model->obtener_total_anticipos_orden($orp_id);
        $anticipos          = $this->Comprobante_model->obtener_anticipos_orden($orp_id);

        $data['titulo']             = 'Nuevo Comprobante desde Orden';
        $data['tipos_comprobante']  = $this->Comprobante_model->listar_tipos_comprobante_venta();
        $data['formas_pago']        = $this->Comprobante_model->listar_formas_pago();
        $data['tributo_igv']        = $this->Comprobante_model->obtener_tributo_igv();
        $data['almacenes']          = $this->Comprobante_model->listar_almacenes();

        $data['orden']              = $orden;
        $data['detalle_orden']      = $detalle;
        $data['retencion_cliente']  = $retencionCliente;
        $data['total_anticipos']    = $totalAnticipos;
        $data['anticipos']          = $anticipos;

        $this->layout('ventas/comprobante/create_desde_orden', $data);
    }

    public function create_anticipo_desde_orden($orp_id) {
        $this->validar_permiso('comprobante_crear');

        $orden = $this->Orden_pedido_model->obtener_para_facturacion($orp_id);

        if (!$orden) {
            show_404();
        }

        $retencionCliente = $this->Comprobante_model->obtener_retencion_cliente($orden->cliente_id);
        $totalAnticipos = $this->Comprobante_model->obtener_total_anticipos_orden($orp_id);
        $anticipos = $this->Comprobante_model->obtener_anticipos_orden($orp_id);

        $data['titulo']             = 'Nuevo Anticipo desde Orden';
        $data['tipos_comprobante']  = $this->Comprobante_model->listar_tipos_comprobante_venta();
        $data['formas_pago']        = $this->Comprobante_model->listar_formas_pago();
        $data['tributo_igv']        = $this->Comprobante_model->obtener_tributo_igv();
        $data['orden']              = $orden;
        $data['retencion_cliente']  = $retencionCliente;
        $data['total_anticipos']    = $totalAnticipos;
        $data['anticipos']          = $anticipos;

        $this->layout('ventas/comprobante/create_anticipo_desde_orden', $data);
    }

    public function store_anticipo_desde_orden($orp_id) {
        $this->validar_permiso('comprobante_crear');

        $orden = $this->Orden_pedido_model->obtener_para_facturacion($orp_id);

        if (!$orden) {
            show_404();
        }

        $this->form_validation->set_rules('comprobante_id', 'Tipo de comprobante', 'required');
        $this->form_validation->set_rules('fecha', 'Fecha', 'required');
        $this->form_validation->set_rules('fp_id', 'Forma de pago', 'required');
        $this->form_validation->set_rules('monto_anticipo', 'Monto de anticipo', 'required|numeric|greater_than[0]');

        if ($this->form_validation->run() == FALSE) {
            return $this->create_anticipo_desde_orden($orp_id);
        }

        $comprobante_id = (int)$this->input->post('comprobante_id', TRUE);
        $cliente_id = (int)$orden->cliente_id;
        $fp_id = (int)$this->input->post('fp_id', TRUE);
        $montoAnticipo = round((float)$this->input->post('monto_anticipo', TRUE), 2);

        $cliente = $this->Comprobante_model->obtener_cliente($cliente_id);
        $tipoComprobante = $this->Comprobante_model->obtener_tipo_comprobante($comprobante_id);
        $tributo = $this->Comprobante_model->obtener_tributo_igv();
        $formaPago = $this->Comprobante_model->obtener_forma_pago($fp_id);

        if (!$cliente || !$tipoComprobante || !$tributo || !$formaPago) {
            $this->session->set_flashdata('error', 'Datos inválidos para generar anticipo');
            redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
        }

        $fecha = $this->input->post('fecha', TRUE);
        $fecha_db = date('Y-m-d H:i:s', strtotime($fecha));

        $modoIncluyeIgv = ((int)$tributo->modo === 1);
        $igvPorcentaje = (float)$tributo->valor_porcentual;

        if ($modoIncluyeIgv) {
            $total = $montoAnticipo;
            $subtotal = round($total / (1 + ($igvPorcentaje / 100)), 2);
            $igv = round($total - $subtotal, 2);
        } else {
            $subtotal = $montoAnticipo;
            $igv = round($subtotal * ($igvPorcentaje / 100), 2);
            $total = round($subtotal + $igv, 2);
        }

        $retencion = null;
        $detraccion = null;
        $importeRetencion = 0;
        $importeDetraccion = 0;
        $pagoNeto = $total;

        $esFactura = ((int)$tipoComprobante->id === 1);
        $esCredito = (strtoupper(trim($formaPago->codigo)) === 'CREDITO' || strtoupper(trim($formaPago->nombre)) === 'CREDITO');

        // Para anticipo: no aplicamos detracción porque no mueve cantidades/productos reales.
        // Retención sí puede evaluarse si tu negocio lo requiere. Por ahora la mantenemos igual al create normal.
        if ($esFactura) {
            $retencion = $this->Comprobante_model->obtener_retencion_cliente($cliente_id);

            if ($retencion && (float)$total >= (float)$retencion->monto_minimo) {
                $importeRetencion = round($total * ((float)$retencion->porcentaje / 100), 2);
                $pagoNeto = round($total - $importeRetencion, 2);
            }
        }

        $fecvencimiento = date('Y-m-d', strtotime($fecha));
        $cuotasGuardar = array();

        if ($esFactura && $esCredito) {
            $cuota_fecha = $this->input->post('cuota_fecha');
            $cuota_importe = $this->input->post('cuota_importe');

            if (empty($cuota_fecha) || empty($cuota_importe) || !is_array($cuota_fecha) || !is_array($cuota_importe)) {
                $this->session->set_flashdata('error', 'Debe ingresar al menos una cuota para factura al crédito');
                redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
            }

            $sumaCuotas = 0;
            $nroCuota = 1;

            for ($i = 0; $i < count($cuota_fecha); $i++) {
                $vencto = !empty($cuota_fecha[$i]) ? $cuota_fecha[$i] : null;
                $importe = isset($cuota_importe[$i]) ? (float)$cuota_importe[$i] : 0;

                if (empty($vencto) || $importe <= 0) {
                    continue;
                }

                $sumaCuotas += $importe;

                $cuotasGuardar[] = array(
                    'nro_cuota' => $nroCuota,
                    'vencto_cuota' => $vencto,
                    'importe_cuota' => round($importe, 2)
                );

                $nroCuota++;
            }

            if (empty($cuotasGuardar)) {
                $this->session->set_flashdata('error', 'Debe ingresar cuotas válidas');
                redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
            }

            if (round($sumaCuotas, 2) != round($pagoNeto, 2)) {
                $this->session->set_flashdata('error', 'La suma de cuotas debe ser igual al pago neto del comprobante');
                redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
            }

            $ultimasFechas = array_column($cuotasGuardar, 'vencto_cuota');
            rsort($ultimasFechas);
            $fecvencimiento = $ultimasFechas[0];
        }

        $this->db->trans_begin();

        $tipoComprobanteActual = $this->Comprobante_model->obtener_tipo_comprobante_for_update($comprobante_id);

        if (!$tipoComprobanteActual) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo obtener el correlativo del comprobante');
            redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
        }

        $serie = trim($tipoComprobanteActual->serie);
        $correlativoActual = (int)$tipoComprobanteActual->correlativo_actual;
        $numero = str_pad($correlativoActual, 8, '0', STR_PAD_LEFT);

        $descripcionAnticipo = 'ANTICIPO SEGUN ORDEN DE PEDIDO N° ' . $orden->id;

        $cabecera = array(
            'comprobante_id'        => $tipoComprobanteActual->id,
            'serie'                 => $serie,
            'numero'                => $numero,
            'cliente_id'            => $cliente->id,
            'fecha'                 => $fecha_db,
            'ordcomp_cliente'       => !empty($orden->orp_ordcomp_cliente) ? $orden->orp_ordcomp_cliente : null,
            'tipocambio'            => (float)$this->input->post('tipocambio', TRUE),
            'subtotal'              => $subtotal,
            'anticipo'              => 0,
            'igv_porcentaje'        => $igvPorcentaje,
            'igv'                   => $igv,
            'otrostributos'         => 0,
            'total'                 => $total,
            'pago_neto'             => $pagoNeto,
            'observaciones'         => trim($this->input->post('observaciones', TRUE)),
            'fecvencimiento'        => $fecvencimiento,
            'origen'                => 'ORDEN',
            'fp_id'                 => $formaPago->id,
            'moneda_id'             => $cliente->moneda_id,
            'vendedor_id'           => $cliente->vendedor_id,
            'estado_id'             => 23,
            'created_by'            => $this->session->userdata('id')
        );

        $comprobId = $this->Comprobante_model->insertar($cabecera);

        if (!$comprobId) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo guardar la cabecera del anticipo');
            redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
        }

        // Detalle único del anticipo
        $this->Comprobante_model->insertar_detalle(array(
            'comprob_id' => $comprobId,
            'producto_id' => 1, // si no quieres usar un producto real, luego lo ajustamos
            'descripcion' => $descripcionAnticipo,
            'cantidad' => 1,
            'precio_unitario' => round($subtotal, 4),
            'subtotal' => round($subtotal, 2)
        ));

        if (!empty($cuotasGuardar)) {
            foreach ($cuotasGuardar as $cuota) {
                $this->Comprobante_model->insertar_cuota(array(
                    'comprob_id' => $comprobId,
                    'nro_cuota' => $cuota['nro_cuota'],
                    'vencto_cuota' => $cuota['vencto_cuota'],
                    'importe_cuota' => $cuota['importe_cuota']
                ));
            }
        }

        if ($importeRetencion > 0 && $retencion) {
            $this->Comprobante_model->insertar_retencion(array(
                'comprob_id' => $comprobId,
                'total' => $total,
                'porcentaje' => (float)$retencion->porcentaje,
                'importe_retencion' => $importeRetencion
            ));
        }

        $this->Comprobante_model->insertar_estado(array(
            'comprob_id'        => $comprobId,
            'estado_id'         => 23,
            'comprob_fechaestado' => date('Y-m-d H:i:s'),
            'usuario_id'        => $this->session->userdata('id')
        ));

        // Relación general con la orden
        $this->Comprobante_model->insertar_relacion_orden(array(
            'comprob_id'        => $comprobId,
            'orp_id'            => $orp_id,
            'tipo_relacion'     => 'ANTICIPO',
            'monto_aplicado'    => $total,
            'estado'            => 1,
            'created_by'        => $this->session->userdata('id')
        ));

        $this->Comprobante_model->actualizar_correlativo_tipo_comprobante(
            $tipoComprobanteActual->id,
            $correlativoActual + 1
        );

        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo registrar el anticipo');
            redirect('ventas/comprobante/create_anticipo_desde_orden/'.$orp_id);
        }

        $this->db->trans_commit();

        $this->session->set_flashdata('success', 'Anticipo generado correctamente: ' . $serie . '-' . $numero);
        redirect('ventas/comprobante/ver/'.$comprobId);
    }

    public function anular($id) {
        $this->validar_permiso('comprobante_anular');

        $comprobante = $this->Comprobante_model->obtener($id);

        if (!$comprobante) {
            show_404();
        }

        $ESTADO_ANULADO = 24;
        $ESTADO_ENVIADO = 27;

        // Si ya está anulado
        if ((int)$comprobante->estado_id === $ESTADO_ANULADO) {
            $this->session->set_flashdata('error', 'El comprobante ya se encuentra anulado');
            redirect('ventas/comprobante');
        }

        // Si ya fue enviado a SUNAT, no permitir anulación directa
        if ((int)$comprobante->estado_id === $ESTADO_ENVIADO) {
            $this->session->set_flashdata('error', 'El comprobante ya fue enviado a SUNAT y no puede anularse directamente. Debe revertirse mediante Nota de Crédito.');
            redirect('ventas/comprobante');
        }

        $this->db->trans_begin();

        // Revertir parte de salida vinculado
        $ps = $this->db
            ->where('comprob_id', $id)
            ->where('estado', 1)
            ->get('lt_parte_salida')
            ->row();

        if ($ps) {
            $detalle_ps = $this->ParteSalida_model->obtener_detalle($ps->id);
            foreach ($detalle_ps as $d) {
                if ($d->maneja_stock) {
                    $this->ParteSalida_model->revertir_stock(
                        $d->producto_id,
                        $ps->almacen_id,
                        $d->cantidad
                    );
                }
            }
            $this->ParteSalida_model->anular($ps->id, $this->session->userdata('id'));
        }

        // 1. Marcar comprobante como anulado
        $this->Comprobante_model->actualizar($id, array(
            'estado_id'    => $ESTADO_ANULADO,
            'saldo'        => 0,
            'estado_cobro' => 2  // o dejarlo como está, solo marcar anulado
        ));

        // 2. Registrar historial de estado
        $this->Comprobante_model->insertar_estado(array(
            'comprob_id'            => $id,
            'estado_id'             => $ESTADO_ANULADO,
            'comprob_fechaestado'   => date('Y-m-d H:i:s'),
            'usuario_id'            => $this->session->userdata('id')
        ));

        // 3. Revisar si tiene relación con orden
        $relacionOrden = $this->Comprobante_model->obtener_relacion_orden($id);

        if ($relacionOrden) {
            $orp_id = (int)$relacionOrden->orp_id;
            $tipoRelacion = trim($relacionOrden->tipo_relacion);

            // 3.1 Desactivar relación general
            $this->Comprobante_model->desactivar_relacion_orden($id);

            // 3.2 Si era comprobante que movía cantidades, desactivar detalle y recalcular saldo
            if ($tipoRelacion !== 'ANTICIPO') {
                $relacionesDetalle = $this->Comprobante_model->obtener_relaciones_orden_detalle($id);

                $this->Comprobante_model->desactivar_relacion_orden_detalle($id);

                foreach ($relacionesDetalle as $relDet) {
                    $orpd_id = (int)$relDet->orpd_id;

                    $detalleOrden = $this->db
                        ->where('id', $orpd_id)
                        ->get('vt_orden_pedido_detalle')
                        ->row();

                    if ($detalleOrden) {
                        $facturado = $this->Comprobante_model->obtener_total_facturado_detalle_orden($orpd_id);
                        $nuevoSaldo = round((float)$detalleOrden->orpd_cantidad - (float)$facturado, 2);

                        if ($nuevoSaldo < 0) {
                            $nuevoSaldo = 0;
                        }

                        $this->Orden_pedido_model->actualizar_saldo_detalle($orpd_id, $nuevoSaldo);
                    }
                }
            }

            // 3.3 Recalcular estado de la orden
            $this->Orden_pedido_model->recalcular_estado_orden($orp_id);
        }

        if ($this->db->trans_status() === FALSE) {
            $this->db->trans_rollback();
            $this->session->set_flashdata('error', 'No se pudo anular el comprobante');
            redirect('ventas/comprobante');
        }

        $this->db->trans_commit();

        $this->session->set_flashdata('success', 'Comprobante anulado correctamente');
        redirect('ventas/comprobante');
    }

    public function create_desde_guia() {
        $this->validar_permiso('comprobante_crear');

        $guiasIds = $this->input->get('guias');

        if (empty($guiasIds) || !is_array($guiasIds)) {
            $this->session->set_flashdata('error', 'Debe seleccionar al menos una guía');
            redirect('almacen/guiaremitente');
        }

        $guiasIds = array_map('intval', $guiasIds);

        $guias = $this->Guiaremitente_model->obtener_para_comprobante($guiasIds);

        if (empty($guias)) {
            $this->session->set_flashdata('error', 'No se encontraron guías válidas');
            redirect('almacen/guiaremitente');
        }

        $clienteId = (int)$guias[0]->cliente_id;
        $orpId = (int)$guias[0]->orp_id;

        foreach ($guias as $g) {
            if ((int)$g->estado_id !== 29) {
                $this->session->set_flashdata('error', 'Solo se pueden facturar guías en estado EMITIDO');
                redirect('almacen/guiaremitente');
            }

            if ((int)$g->cliente_id !== $clienteId) {
                $this->session->set_flashdata('error', 'Todas las guías deben pertenecer al mismo cliente');
                redirect('almacen/guiaremitente');
            }

            if ((int)$g->orp_id !== $orpId) {
                $this->session->set_flashdata('error', 'Todas las guías deben pertenecer a la misma orden de pedido');
                redirect('almacen/guiaremitente');
            }
        }

        $detalle = $this->Guiaremitente_model->obtener_detalle_para_comprobante($guiasIds);

        if (empty($detalle)) {
            $this->session->set_flashdata('error', 'Las guías seleccionadas no tienen detalle');
            redirect('almacen/guiaremitente');
        }

        $retencionCliente = $this->Comprobante_model->obtener_retencion_cliente($clienteId);
        $totalAnticipos = $this->Comprobante_model->obtener_total_anticipos_orden($orpId);
        $anticipos = $this->Comprobante_model->obtener_anticipos_orden($orpId);

        $data['titulo']             = 'Nuevo Comprobante desde Guía';
        $data['guias']              = $guias;
        $data['guias_ids']          = $guiasIds;
        $data['detalle_guia']       = $detalle;
        $data['orden_id']           = $orpId;

        $data['tipos_comprobante']  = $this->Comprobante_model->listar_tipos_comprobante_venta();
        $data['formas_pago']        = $this->Comprobante_model->listar_formas_pago();
        $data['tributo_igv']        = $this->Comprobante_model->obtener_tributo_igv();
        $data['almacenes']          = $this->Comprobante_model->listar_almacenes();
        $data['retencion_cliente']  = $retencionCliente;
        $data['total_anticipos']    = $totalAnticipos;
        $data['anticipos']          = $anticipos;

        $this->layout('ventas/comprobante/create_desde_guia', $data);
    }

}