<?php
require __DIR__ . '/includes.php';

function estados_colaborador(){ return ['Prospecto','En selección','Activo','Contratado','Retirado','Suspendido','Inactivo','Candidato no seleccionado']; }
function tipos_contrato(){ return ['Laboral','Prestación de servicios','Aprendizaje','Otro']; }
function salario_mods(){ return ['Fijo','Variable']; }
function categories(){ return ['Ingreso','Retiro','Vacaciones','Ajuste salarial','Liquidación prima de servicios','Liquidación cesantías y/o Int. Ces.','Liquidación de contrato','Incapacidad laboral','Incapacidad general','Licencia remunerada','Licencia no remunerada','Préstamo','Seguridad social']; }
function novelty_text_key($v){
    $v=mb_strtolower(trim((string)$v),'UTF-8');
    $v=strtr($v,['á'=>'a','é'=>'e','í'=>'i','ó'=>'o','ú'=>'u','ü'=>'u','ñ'=>'n']);
    return preg_replace('/\s+/u',' ',$v);
}
function text_has($text,$needle){ return mb_strpos((string)$text,(string)$needle,0,'UTF-8')!==false; }
function canonical_novelty_category($categoria,$tipo=''){
    $cat=novelty_text_key($categoria); $type=novelty_text_key($tipo); $text=trim($cat.' '.$type);
    $canon=[]; foreach(categories() as $c){ $canon[novelty_text_key($c)]=$c; }
    if(isset($canon[$cat])) return $canon[$cat];
    if(text_has($text,'prima')) return 'Liquidación prima de servicios';
    if(text_has($text,'cesant') || text_has($text,'interes ces') || text_has($text,'int ces')) return 'Liquidación cesantías y/o Int. Ces.';
    if(text_has($text,'liquidacion') && (text_has($text,'contrato') || text_has($text,'final'))) return 'Liquidación de contrato';
    if($cat==='liquidacion') return 'Liquidación de contrato';
    if(text_has($text,'vacacion')) return 'Vacaciones';
    if(text_has($text,'incapacidad')){
        if(text_has($text,'laboral') || text_has($text,'arl') || text_has($text,'accidente de trabajo') || text_has($text,'enfermedad laboral')) return 'Incapacidad laboral';
        return 'Incapacidad general';
    }
    if(text_has($text,'no remuner') || text_has($text,'sin remuner') || text_has($text,'lcno remuner') || text_has($text,'lic no remuner')) return 'Licencia no remunerada';
    if((text_has($text,'remunerada') || text_has($text,'remunerado')) && (text_has($text,'lic') || text_has($text,'permiso'))) return 'Licencia remunerada';
    if(text_has($text,'licencia') || text_has($text,'permiso') || text_has($text,'ausencia') || text_has($text,'suspension')){
        if(text_has($text,'no remuner') || text_has($text,'sin remuner') || text_has($text,'no pago') || text_has($text,'ausencia') || text_has($text,'suspension')) return 'Licencia no remunerada';
        return 'Licencia remunerada';
    }
    if(text_has($text,'prestamo')) return 'Préstamo';
    if(text_has($text,'seguridad social') || text_has($text,'afiliacion') || text_has($text,'traslado eps') || text_has($text,'traslado afp') || preg_match('/\b(eps|afp|arl|ccf)\b/u',$text)) return 'Seguridad social';
    if(text_has($text,'salario') || text_has($text,'salarial') || text_has($text,'hora extra') || text_has($text,'recargo') || text_has($text,'comision') || text_has($text,'bonificacion')) return 'Ajuste salarial';
    if(text_has($text,'retiro') || text_has($text,'desvincul') || text_has($text,'terminacion') || text_has($text,'finalizacion')) return 'Retiro';
    if(text_has($text,'ingreso') || text_has($text,'reingreso') || text_has($text,'vinculacion') || text_has($text,'contratacion') || in_array($cat,['seleccion','cambio contractual','cambio de estado'],true)) return 'Ingreso';
    if($cat==='salario' || $cat==='horas extras') return 'Ajuste salarial';
    if($cat==='prima de servicios') return 'Liquidación prima de servicios';
    if(in_array($cat,['cesantias','intereses cesantias'],true)) return 'Liquidación cesantías y/o Int. Ces.';
    if($cat==='incapacidad') return 'Incapacidad general';
    if(in_array($cat,['licencia','permiso'],true)) return 'Licencia remunerada';
    if($cat==='ausencia') return 'Licencia no remunerada';
    if($cat==='seguridad social') return 'Seguridad social';
    if(in_array($cat,['vinculacion','seleccion','cambio contractual','cambio de estado'],true)) return 'Ingreso';
    return null;
}
function novelty_category_sql_expr($alias='n'){
    $p=$alias ? $alias.'.' : '';
    $txt="LOWER(CONCAT_WS(' ',COALESCE({$p}categoria,''),COALESCE({$p}tipo_novedad,'')))";
    return "CASE
      WHEN $txt LIKE '%prima%' THEN 'Liquidación prima de servicios'
      WHEN $txt LIKE '%cesant%' OR $txt LIKE '%interes ces%' OR $txt LIKE '%int ces%' THEN 'Liquidación cesantías y/o Int. Ces.'
      WHEN $txt LIKE '%liquidaci%' AND ($txt LIKE '%contrato%' OR $txt LIKE '%final%') THEN 'Liquidación de contrato'
      WHEN LOWER(COALESCE({$p}categoria,''))='liquidación' OR LOWER(COALESCE({$p}categoria,''))='liquidacion' THEN 'Liquidación de contrato'
      WHEN $txt LIKE '%vacacion%' THEN 'Vacaciones'
      WHEN $txt LIKE '%incapacidad%' AND ($txt LIKE '%laboral%' OR $txt LIKE '%arl%' OR $txt LIKE '%accidente de trabajo%' OR $txt LIKE '%enfermedad laboral%') THEN 'Incapacidad laboral'
      WHEN $txt LIKE '%incapacidad%' THEN 'Incapacidad general'
      WHEN $txt LIKE '%no remuner%' OR $txt LIKE '%sin remuner%' OR $txt LIKE '%lcno remuner%' OR $txt LIKE '%lic no remuner%' THEN 'Licencia no remunerada'
      WHEN ($txt LIKE '%remunerada%' OR $txt LIKE '%remunerado%') AND ($txt LIKE '%lic%' OR $txt LIKE '%permiso%') THEN 'Licencia remunerada'
      WHEN ($txt LIKE '%licencia%' OR $txt LIKE '%permiso%' OR $txt LIKE '%ausencia%' OR $txt LIKE '%suspensi%') AND ($txt LIKE '%no remuner%' OR $txt LIKE '%sin remuner%' OR $txt LIKE '%no pago%' OR $txt LIKE '%ausencia%' OR $txt LIKE '%suspensi%') THEN 'Licencia no remunerada'
      WHEN $txt LIKE '%licencia%' OR $txt LIKE '%permiso%' THEN 'Licencia remunerada'
      WHEN $txt LIKE '%préstamo%' OR $txt LIKE '%prestamo%' THEN 'Préstamo'
      WHEN $txt LIKE '%seguridad social%' OR $txt LIKE '%afiliaci%' OR $txt LIKE '%traslado eps%' OR $txt LIKE '%traslado afp%' OR $txt REGEXP '(^|[[:space:]])(eps|afp|arl|ccf)([[:space:]]|$)' THEN 'Seguridad social'
      WHEN $txt LIKE '%salario%' OR $txt LIKE '%salarial%' OR $txt LIKE '%hora extra%' OR $txt LIKE '%recargo%' OR $txt LIKE '%comisi%' OR $txt LIKE '%bonificaci%' THEN 'Ajuste salarial'
      WHEN $txt LIKE '%retiro%' OR $txt LIKE '%desvincul%' OR $txt LIKE '%terminaci%' OR $txt LIKE '%finalizaci%' THEN 'Retiro'
      WHEN $txt LIKE '%ingreso%' OR $txt LIKE '%reingreso%' OR $txt LIKE '%vinculaci%' OR $txt LIKE '%contrataci%' OR LOWER(COALESCE({$p}categoria,'')) IN ('selección','seleccion','cambio contractual','cambio de estado') THEN 'Ingreso'
      ELSE {$p}categoria END";
}
function arrval($data,$keys,$default=null){ foreach((array)$keys as $k){ if(isset($data[$k]) && trim((string)$data[$k])!=='') return trim((string)$data[$k]); } return $default; }
function normalize_money($v){ $v=trim((string)$v); if($v==='') return null; $v=str_replace(['$',' '], '', $v); if(strpos($v, ',')!==false){ $v=str_replace('.', '', $v); $v=str_replace(',', '.', $v); } else { $dot=substr_count($v,'.'); if($dot>1){ $v=str_replace('.','',$v); } elseif($dot===1){ [$a,$b]=explode('.',$v,2); if(strlen($b)===3) $v=$a.$b; } } return is_numeric($v)?$v:null; }
function category_filter_sql($alias,$categoria,&$params){ $categoria=trim((string)$categoria); if($categoria==='') return ''; $params[]=$categoria; return ' AND ('.novelty_category_sql_expr($alias).')=?'; }
function novelty_select_columns(){ return "n.*, COALESCE(NULLIF(c.nombre_completo,''), NULLIF(n.colaborador_text,'')) AS nombre_completo, COALESCE(NULLIF(c.numero_documento,''), NULLIF(n.documento_text,'')) AS numero_documento, c.cargo, c.area, co.nombre company_name"; }
function clean_doc_key($doc){ return preg_replace('/[^A-Za-z0-9]/','',trim((string)$doc)); }
function find_collaborator_by_document($company_id,$doc){ $doc=trim((string)$doc); if($doc==='') return null; $clean=clean_doc_key($doc); $sql="SELECT id,nombre_completo,numero_documento FROM collaborators WHERE company_id=? AND (numero_documento=? OR REPLACE(REPLACE(REPLACE(REPLACE(numero_documento,'.',''),',',''),' ',''),'-','')=?) ORDER BY CASE WHEN numero_documento=? THEN 0 ELSE 1 END, id DESC LIMIT 1"; $st=db()->prepare($sql); $st->execute([$company_id,$doc,$clean,$doc]); $r=$st->fetch(); return $r ?: null; }

function read_csv_rows($tmp){
    $fh=fopen($tmp,'r'); if(!$fh) return [];
    $first=fgets($fh); if($first===false){ fclose($fh); return []; }
    $first=preg_replace('/^\xEF\xBB\xBF/','',$first);
    $delims=[';'=>substr_count($first,';'), ','=>substr_count($first,','), "\t"=>substr_count($first,"\t")];
    arsort($delims); $delim=array_key_first($delims); if(($delims[$delim] ?? 0)===0) $delim=';';
    rewind($fh);
    $head=fgetcsv($fh,0,$delim); if(!$head){ fclose($fh); return []; }
    $head=array_map(function($h){ $h=preg_replace('/^\xEF\xBB\xBF/','',(string)$h); return trim(mb_strtolower($h,'UTF-8')); },$head);
    $rows=[];
    while(($r=fgetcsv($fh,0,$delim))!==false){
        if(count(array_filter($r, fn($v)=>trim((string)$v)!==''))===0) continue;
        $row=[]; foreach($head as $i=>$h){ if($h==='') continue; $row[$h]=$r[$i]??''; }
        $rows[]=$row;
    }
    fclose($fh); return $rows;
}
function csv_date($v){
    $v=trim((string)$v); if($v==='' || strtoupper($v)==='NA' || strtoupper($v)==='N/A') return null;
    if(preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/',$v,$m)) return sprintf('%04d-%02d-%02d',$m[1],$m[2],$m[3]);
    if(preg_match('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/',$v,$m)) return sprintf('%04d-%02d-%02d',$m[3],$m[2],$m[1]);
    if(preg_match('/^(\d{1,2})-(\d{1,2})-(\d{4})$/',$v,$m)) return sprintf('%04d-%02d-%02d',$m[3],$m[2],$m[1]);
    $ts=strtotime($v); return $ts?date('Y-m-d',$ts):null;
}
function table_columns($table){
    static $cache=[]; if(isset($cache[$table])) return $cache[$table];
    $st=db()->prepare('SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME=?');
    $st->execute([$table]); return $cache[$table]=array_column($st->fetchAll(),'COLUMN_NAME');
}
function upsert_collaborator_from_csv($company_id,$data,$user_id){
    $doc=arrval($data,['numero_documento','número de id','numero de id','documento','id'],''); if(!$doc) return false;
    $cols=table_columns('collaborators');
    $map=[
        'company_id'=>$company_id,
        'tipo_documento'=>arrval($data,['tipo_documento','tipo de id'],'CC'),
        'numero_documento'=>$doc,
        'fecha_expedicion_documento'=>csv_date(arrval($data,['fecha_expedicion_documento','expedición','expedicion','fecha expedicion','fecha de expedicion'],'')),
        'nombre_completo'=>arrval($data,['nombre_completo','nombre','colaborador'],''),
        'direccion'=>arrval($data,['direccion','dirección'],''),
        'telefono'=>arrval($data,['telefono','teléfono','celular'],''),
        'correo_electronico'=>arrval($data,['correo_electronico','correo electrónico','correo','email'],''),
        'area'=>arrval($data,['area','área'],''),
        'cargo'=>arrval($data,['cargo'],''),
        'tipo_vinculacion'=>arrval($data,['tipo_contrato','contrato','tipo_vinculacion'],'Laboral'),
        'fecha_ingreso'=>csv_date(arrval($data,['fecha_ingreso','ingreso'],'')),
        'fecha_retiro'=>csv_date(arrval($data,['fecha_retiro','retiro'],'')),
        'estado'=>arrval($data,['estado'],'Activo'),
        'salario_modalidad'=>arrval($data,['salario_modalidad','salario mod','salario mod.'],'Fijo'),
        'salario_base'=>normalize_money(arrval($data,['salario','salario_base','salario $'],'')),
        'eps'=>arrval($data,['eps'],''),
        'afp'=>arrval($data,['afp'],''),
        'arl'=>arrval($data,['arl'],''),
        'caja_compensacion'=>arrval($data,['ccf','caja_compensacion','caja compensacion'],''),
        'fondo_cesantias'=>arrval($data,['cesantias','cesantías','fondo_cesantias'],''),
        'created_by'=>$user_id
    ];
    $use=[]; foreach($map as $k=>$v){ if(in_array($k,$cols,true)) $use[$k]=$v; }
    $names=array_keys($use); $ph=implode(',',array_fill(0,count($names),'?'));
    $updates=[]; foreach($names as $n){ if(!in_array($n,['company_id','numero_documento','created_by'],true)) $updates[]="$n=VALUES($n)"; }
    $sql='INSERT INTO collaborators('.implode(',',$names).') VALUES('.$ph.') ON DUPLICATE KEY UPDATE '.implode(',',$updates);
    db()->prepare($sql)->execute(array_values($use)); return true;
}
function import_page(){
    require_write(); $u=current_user(); $canAll=function_exists('can_manage_all_companies') ? can_manage_all_companies() : is_super();
    if($_SERVER['REQUEST_METHOD']==='POST'){
        check_csrf();
        $company_id=$canAll ? (int)($_POST['company_id'] ?? 0) : (int)($u['company_id'] ?: ($_POST['company_id'] ?? 0));
        if(!$company_id){ flash('err','Debe seleccionar una empresa para importar.'); header('Location: '.base_url('index.php?page=import')); exit; }
        $type=$_POST['type'] ?? 'collaborators'; $count=0; $skipped=0;
        if(empty($_FILES['csv_file']['tmp_name'])){ flash('err','Debe seleccionar un archivo CSV.'); header('Location: '.base_url('index.php?page=import')); exit; }
        try{
            foreach(read_csv_rows($_FILES['csv_file']['tmp_name']) as $data){
                if($type==='collaborators'){
                    if(upsert_collaborator_from_csv($company_id,$data,$u['id'])) $count++; else $skipped++;
                } else {
                    $doc=arrval($data,['numero_documento','número de id','numero de id','documento'],'');
                    $found=$doc?find_collaborator_by_document($company_id,$doc):null;
                    $coll=$found['id']??null;
                    $name=arrval($data,['colaborador','nombre'],$found['nombre_completo']??'');
                    $freg=csv_date(arrval($data,['fecha_registro','fecha'],date('Y-m-d'))) ?: date('Y-m-d');
                    $fini=csv_date(arrval($data,['fecha_inicio','inicio/gestión','inicio/gestion','fecha_inicio_gestion'],''));
                    $ffin=csv_date(arrval($data,['fecha_fin','terminación','terminacion'],''));
                    $tipoImportado=arrval($data,['tipo_novedad','tipo'],'Sin clasificar');
                    $categoriaImportada=canonical_novelty_category(arrval($data,['categoria','categoría'],''),$tipoImportado);
                    if(!$categoriaImportada){ $skipped++; continue; }
                    db()->prepare('INSERT INTO novelties(company_id,collaborator_id,documento_text,colaborador_text,fecha_registro,fecha_inicio,fecha_fin,quincena,categoria,tipo_novedad,dias,horas,valor,estado_novedad,descripcion,origen,created_by) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')->execute([$company_id,$coll,$doc,$name,$freg,$fini,$ffin,calc_quincena($fini?:$freg),$categoriaImportada,$tipoImportado,normalize_money(arrval($data,['dias','días'],'')),normalize_money(arrval($data,['horas'],'')),normalize_money(arrval($data,['valor'],'')),arrval($data,['estado_novedad','estado'],'Registrada'),arrval($data,['descripcion','descripción'],''),arrval($data,['origen'],'Importación CSV'),$u['id']]);
                    $count++;
                }
            }
            flash('ok',"Importación finalizada: $count registros procesados".($skipped?"; $skipped filas omitidas por información incompleta o categoría no reconocida.":'.'));
        }catch(Throwable $e){ flash('err','Error en importación: '.$e->getMessage()); }
        header('Location: '.base_url('index.php?page=import')); exit;
    }
    header_html('Importar CSV'); ?>
    <div class="card"><form method="post" enctype="multipart/form-data"><?= csrf_field() ?>
      <div class="grid cols-3"><div class="field"><label>Empresa</label><select name="company_id" <?= user_restricts_company()?'disabled':'' ?>><?php if(!$canAll && !empty($u['company_id'])): ?><option value="<?= e($u['company_id']) ?>"><?= e($u['company_name'] ?? 'Empresa asignada') ?></option><?php else: ?><?= company_options($_GET['company_id'] ?? ($u['company_id']??'')) ?><?php endif; ?></select></div>
      <div class="field"><label>Tipo importación</label><select name="type"><option value="collaborators">Colaboradores</option><option value="novelties">Novedades</option></select></div>
      <div class="field"><label>Archivo CSV</label><input type="file" name="csv_file" accept=".csv" required></div></div>
      <div class="actions"><button class="btn">Importar</button><a class="btn secondary" href="<?= e(base_url('templates/plantilla_colaboradores.csv')) ?>">Plantilla colaboradores</a><a class="btn secondary" href="<?= e(base_url('templates/plantilla_novedades.csv')) ?>">Plantilla novedades</a></div>
    </form></div>
    <div class="card"><h3>Recomendación</h3><p>Para novedades, cargue primero la base de colaboradores. El sistema enlaza por número de documento; si no encuentra coincidencia, conserva el documento y el nombre como texto de respaldo.</p></div>
    <?php footer_html(); }

function create_auto_novelty($company_id,$collaborator_id,$categoria,$tipo,$fecha_inicio=null,$fecha_fin=null,$descripcion=null,$valor=null){ $categoria=canonical_novelty_category($categoria,$tipo) ?: $categoria; $fecha_registro=date('Y-m-d'); $baseDate=$fecha_inicio ?: $fecha_registro; $docText=null; $nameText=null; if($collaborator_id){ $st=db()->prepare('SELECT numero_documento,nombre_completo FROM collaborators WHERE id=? LIMIT 1'); $st->execute([$collaborator_id]); if($c=$st->fetch()){ $docText=$c['numero_documento']; $nameText=$c['nombre_completo']; } } db()->prepare('INSERT INTO novelties(company_id,collaborator_id,documento_text,colaborador_text,fecha_registro,fecha_inicio,fecha_fin,quincena,categoria,tipo_novedad,valor,estado_novedad,descripcion,origen,created_by) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')->execute([$company_id,$collaborator_id,$docText,$nameText,$fecha_registro,$fecha_inicio,$fecha_fin,calc_quincena($baseDate),$categoria,$tipo,$valor,'Registrada',$descripcion,'Generada automáticamente',current_user()['id'] ?? null]); }
function collaborator_has_ingreso_novelty($collaborator_id){
    if(!$collaborator_id) return false;
    $st=db()->prepare("SELECT COUNT(*) FROM novelties WHERE collaborator_id=? AND (categoria='Ingreso' OR tipo_novedad LIKE 'Ingreso%')");
    $st->execute([(int)$collaborator_id]);
    return (int)$st->fetchColumn()>0;
}
function collaborator_is_preliminar_estado($estado){ return in_array((string)$estado, ['Prospecto','En selección','Candidato no seleccionado'], true); }
function collaborator_can_generate_ingreso_from_estado($estado){ return !collaborator_is_preliminar_estado($estado) && in_array((string)$estado, ['Activo','Contratado'], true); }
function report_collaborator_columns(){ return ['Nombre','Tipo ID','Número ID','Expedición','Empresa','Área','Cargo','Contrato','Ingreso','Retiro','Estado','Salario mod.','Salario','EPS','AFP','ARL','CCF','Cesantías']; }
function report_collaborator_row($r){ return [$r['nombre_completo'] ?? '',$r['tipo_documento'] ?? '',$r['numero_documento'] ?? '',dt($r['fecha_expedicion_documento'] ?? ''),$r['company_name'] ?? '',$r['area'] ?? '',$r['cargo'] ?? '',$r['tipo_vinculacion'] ?? '',dt($r['fecha_ingreso'] ?? ''),dt($r['fecha_retiro'] ?? ''),$r['estado'] ?? '',$r['salario_modalidad'] ?? '',money($r['salario_base'] ?? ''),$r['eps'] ?? '',$r['afp'] ?? '',$r['arl'] ?? '',$r['caja_compensacion'] ?? '',$r['fondo_cesantias'] ?? '']; }
function novelty_calendar_events($rows){ $events=[]; foreach($rows as $r){ $base=$r['fecha_inicio'] ?: ($r['fecha_registro'] ?? null); $fin=$r['fecha_fin'] ?? null; if($base){ $events[]=['date'=>$base,'kind'=>'Inicio/gestión','row'=>$r]; } if($fin){ $events[]=['date'=>$fin,'kind'=>'Terminación','row'=>$r]; } } usort($events, fn($a,$b)=>strcmp($a['date'],$b['date'])); return $events; }
function novelty_calendar_months($events){ $months=[]; foreach($events as $e){ $m=date('Y-m', strtotime($e['date'])); $months[$m][]=$e; } ksort($months); return $months; }


function login(){ if(current_user()){ header('Location: '.base_url('index.php?page=dashboard')); exit; } if($_SERVER['REQUEST_METHOD']==='POST'){ $st=db()->prepare('SELECT u.*, co.nombre company_name FROM users u LEFT JOIN companies co ON co.id=u.company_id WHERE u.email=? AND u.estado="Activo" LIMIT 1'); $st->execute([trim($_POST['email'] ?? '')]); $u=$st->fetch(); if($u && password_verify($_POST['password'] ?? '', $u['password_hash'])){ unset($u['password_hash']); $_SESSION['user']=$u; audit('Inicio de sesión'); header('Location: '.base_url('index.php?page=dashboard')); exit; } flash('err','Credenciales inválidas.'); } ?><!doctype html><html lang="es"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Ingresar - <?= e(cfg('app_name')) ?></title><link rel="stylesheet" href="<?= e(base_url('assets/style.css')) ?>"></head><body class="login-page"><div class="login-card"><div class="login-brand"><img src="<?= e(mandatos_logo_url()) ?>" alt="Mandatos"><h1>Mandatos GE</h1><p>Gestión Laboral y Novedades de Personal</p></div><?php show_flash(); ?><form method="post"><div class="field"><label>Correo electrónico</label><input type="email" name="email" required autofocus></div><div class="field"><label>Contraseña</label><input type="password" name="password" required></div><button class="btn" type="submit">Ingresar</button><div class="footer-note">Uso interno. La información laboral y de seguridad social debe tratarse conforme a las políticas de protección de datos aplicables.</div></form></div></body></html><?php }
function logout(){ audit('Cierre de sesión'); session_destroy(); header('Location: '.base_url('index.php?page=login')); exit; }

function dashboard(){ require_login(); $f=user_company_filter_sql(); $params=$f['params'];
    $coll=db()->prepare("SELECT COUNT(*) FROM collaborators WHERE 1=1 {$f['sql']}"); $coll->execute($params); $collCount=$coll->fetchColumn();
    $act=db()->prepare("SELECT COUNT(*) FROM collaborators WHERE estado='Activo' {$f['sql']}"); $act->execute($params); $active=$act->fetchColumn();
    $nov=db()->prepare("SELECT COUNT(*) FROM novelties WHERE 1=1 {$f['sql']}"); $nov->execute($params); $novCount=$nov->fetchColumn();
    $month=db()->prepare("SELECT COUNT(*) FROM novelties WHERE MONTH(fecha_registro)=MONTH(CURDATE()) AND YEAR(fecha_registro)=YEAR(CURDATE()) {$f['sql']}"); $month->execute($params); $monthCount=$month->fetchColumn();
    $recent=db()->prepare("SELECT ".novelty_select_columns()." FROM novelties n LEFT JOIN collaborators c ON c.id=n.collaborator_id JOIN companies co ON co.id=n.company_id WHERE 1=1 ".($f['sql']?str_replace('company_id','n.company_id',$f['sql']):'')." ORDER BY n.created_at DESC LIMIT 8"); $recent->execute($params); $rows=$recent->fetchAll();
    $nearSql="SELECT ".novelty_select_columns().", DATEDIFF(n.fecha_fin, CURDATE()) AS dias_restantes FROM novelties n LEFT JOIN collaborators c ON c.id=n.collaborator_id JOIN companies co ON co.id=n.company_id WHERE n.fecha_fin IS NOT NULL AND n.fecha_fin >= CURDATE() AND n.fecha_fin <= DATE_ADD(CURDATE(), INTERVAL 7 DAY) ".($f['sql']?str_replace('company_id','n.company_id',$f['sql']):'')." ORDER BY n.fecha_fin ASC, n.id DESC LIMIT 50";
    $near=db()->prepare($nearSql); $near->execute($params); $nearRows=$near->fetchAll();
    $todayCount=count(array_filter($nearRows, fn($r)=>(int)$r['dias_restantes']===0));
    header_html('Panel principal'); ?>
    <style id="dashboard-alert-force-style">
      .dashboard-alert-card-strong{position:relative!important;overflow:hidden!important;border:3px solid #f97316!important;border-left:18px solid #dc2626!important;background:linear-gradient(110deg,#fff1f2 0%,#fff7ed 48%,#ecfdf5 100%)!important;box-shadow:0 24px 55px rgba(220,38,38,.20),0 12px 32px rgba(1,73,94,.14)!important;padding:22px!important;}
      .dashboard-alert-card-strong:before{content:'';position:absolute;top:0;left:0;right:0;height:8px;background:linear-gradient(90deg,#dc2626,#f97316,#eab308,#16a34a)!important;}
      .dashboard-alert-card-strong .dash-alert-head{display:flex!important;align-items:center!important;justify-content:space-between!important;gap:16px!important;margin-bottom:14px!important;}
      .dashboard-alert-card-strong .dash-alert-icon{display:inline-flex!important;width:42px!important;height:42px!important;border-radius:50%!important;align-items:center!important;justify-content:center!important;background:#dc2626!important;color:#fff!important;font-size:24px!important;font-weight:950!important;box-shadow:0 10px 25px rgba(220,38,38,.30)!important;}
      .dashboard-alert-card-strong h3{margin:0!important;color:#7f1d1d!important;font-size:20px!important;font-weight:950!important;}
      .dashboard-alert-card-strong p{margin:4px 0 0!important;color:#7c2d12!important;font-weight:750!important;}
      .dashboard-alert-card-strong .dash-alert-badge{display:inline-flex!important;align-items:center!important;justify-content:center!important;padding:13px 22px!important;border-radius:999px!important;background:linear-gradient(135deg,#dc2626,#ef4444)!important;color:#fff!important;font-size:18px!important;font-weight:950!important;box-shadow:0 14px 25px rgba(220,38,38,.25)!important;}
      .dashboard-alert-card-strong .dash-alert-strip{display:flex!important;gap:12px!important;flex-wrap:wrap!important;margin:14px 0 12px!important;}
      .dashboard-alert-card-strong .alert-tone{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:92px!important;padding:10px 18px!important;border-radius:999px!important;color:#fff!important;font-weight:950!important;font-size:15px!important;box-shadow:0 10px 20px rgba(0,0,0,.12)!important;}
      .dashboard-alert-card-strong .alert-tone-0{background:#dc2626!important}.dashboard-alert-card-strong .alert-tone-2{background:#f97316!important}.dashboard-alert-card-strong .alert-tone-4{background:#eab308!important}.dashboard-alert-card-strong .alert-tone-7{background:#16a34a!important}
      .dashboard-alert-card-strong .dashboard-alert-table{border:2px solid rgba(220,38,38,.16)!important;border-radius:18px!important;box-shadow:0 14px 30px rgba(1,73,94,.12)!important;background:rgba(255,255,255,.72)!important;}
      .dashboard-alert-table table{border-collapse:separate!important;border-spacing:0 8px!important;background:transparent!important;}
      .dashboard-alert-table thead th{background:linear-gradient(180deg,#7f1d1d,#01495e)!important;color:#fff!important;font-size:12px!important;}
      .dashboard-alert-table tbody tr.alert-severity-0 td{background:#fecaca!important}.dashboard-alert-table tbody tr.alert-severity-1 td{background:#fed7aa!important}.dashboard-alert-table tbody tr.alert-severity-2 td{background:#ffedd5!important}.dashboard-alert-table tbody tr.alert-severity-3 td{background:#fef3c7!important}.dashboard-alert-table tbody tr.alert-severity-4 td{background:#fef9c3!important}.dashboard-alert-table tbody tr.alert-severity-5 td{background:#ecfccb!important}.dashboard-alert-table tbody tr.alert-severity-6 td{background:#dcfce7!important}.dashboard-alert-table tbody tr.alert-severity-7 td{background:#bbf7d0!important}
      .dashboard-alert-table tbody tr td{font-size:14px!important;font-weight:750!important;border-top:1px solid rgba(0,0,0,.06)!important;border-bottom:1px solid rgba(0,0,0,.06)!important;}
      .dashboard-alert-table tbody tr td:first-child{border-left:8px solid hsl(var(--alert-hue),82%,42%)!important;border-radius:14px 0 0 14px!important;font-weight:950!important;}
      .dashboard-alert-table tbody tr td:last-child{border-radius:0 14px 14px 0!important;}
      .dashboard-alert-table .alert-days{display:inline-flex!important;align-items:center!important;justify-content:center!important;min-width:92px!important;padding:8px 12px!important;background:#fff!important;border:2px solid hsl(var(--alert-hue),82%,42%)!important;border-radius:999px!important;font-weight:950!important;color:hsl(var(--alert-hue),82%,24%)!important;box-shadow:0 8px 17px hsla(var(--alert-hue),82%,44%,.16)!important;}
      .dashboard-alert-table .alert-date-dot{display:inline-block!important;width:13px!important;height:13px!important;border-radius:50%!important;background:hsl(var(--alert-hue),82%,44%)!important;margin-right:8px!important;box-shadow:0 0 0 5px hsla(var(--alert-hue),82%,54%,.18)!important;}
    </style>
    <div class="grid cols-4">
      <div class="kpi"><div class="num"><?= e($collCount) ?></div><div class="label">Colaboradores registrados</div></div>
      <div class="kpi"><div class="num"><?= e($active) ?></div><div class="label">Colaboradores activos</div></div>
      <div class="kpi"><div class="num"><?= e($novCount) ?></div><div class="label">Novedades históricas</div></div>
      <div class="kpi"><div class="num"><?= e($monthCount) ?></div><div class="label">Novedades del mes</div></div>
    </div>
    <div class="card"><h3>Acciones rápidas</h3><p>Registre información nueva, genere documentos e informes.</p><div class="actions"><a class="btn ok" href="<?= e(base_url('index.php?page=collaborator_form')) ?>">Nuevo colaborador</a><a class="btn" href="<?= e(base_url('index.php?page=novelty_form')) ?>">Nueva novedad</a><a class="btn secondary" href="<?= e(base_url('index.php?page=reports')) ?>">Informe</a><a class="btn secondary" href="<?= e(base_url('index.php?page=indicators')) ?>">Indicadores</a><a class="btn secondary" href="<?= e(base_url('contracts.php')) ?>">Contrato</a><a class="btn secondary" href="<?= e(base_url('index.php?page=liquidations')) ?>">Liquidación</a><a class="btn secondary" href="<?= e(base_url('security_studies.php')) ?>">Estudio seguridad</a></div></div>
    <div class="card dashboard-alert-card dashboard-alert-card-strong">
      <div class="dash-alert-head dash-alert-head-strong">
        <div class="dash-alert-title-wrap">
          <div class="dash-alert-icon">!</div>
          <div>
            <h3>Alertas de finalización de novedades</h3>
            <p>Seguimiento de novedades con fecha de terminación entre hoy y los próximos 7 días.</p>
          </div>
        </div>
        <div class="dash-alert-badge <?= $todayCount>0?'danger':'ok' ?>"><?= e($todayCount) ?> vencen hoy</div>
      </div>
      <div class="dash-alert-strip no-print"><span class="alert-tone alert-tone-0">Hoy</span><span class="alert-tone alert-tone-2">2 días</span><span class="alert-tone alert-tone-4">4 días</span><span class="alert-tone alert-tone-7">Día 7</span></div>
      <?php if(!$nearRows): ?>
        <div class="empty-state">No hay novedades con fecha de terminación en los próximos 7 días.</div>
      <?php else: ?>
        <div class="alert-legend no-print"><span class="lg red"></span> Hoy <span class="lg yellow"></span> Próximos días <span class="lg green"></span> Día 7</div>
        <div class="table-wrap dashboard-alert-table"><table class="sortable"><thead><tr><th>Terminación</th><th>Días</th><th>Empresa</th><th>Colaborador</th><th>Documento</th><th>Categoría</th><th>Tipo</th><th>Estado</th></tr></thead><tbody>
        <?php foreach($nearRows as $r): $d=max(0,min(7,(int)$r['dias_restantes'])); $hue=(int)round(($d/7)*120); $label=$d===0?'Hoy':($d===1?'Mañana':('En '.$d.' días')); ?>
          <tr class="alert-severity-<?= e($d) ?>" style="--alert-hue:<?= e($hue) ?>;--alert-alpha:<?= e(number_format(0.32-($d*0.018),3,'.','')) ?>">
            <td><span class="alert-date-dot"></span><?= e(dt($r['fecha_fin'])) ?></td>
            <td><span class="alert-days"><?= e($label) ?></span></td>
            <td><?= e($r['company_name']) ?></td>
            <td><a href="<?= e(base_url('index.php?page=collaborator_view&id='.$r['collaborator_id'])) ?>"><?= e($r['nombre_completo']) ?></a></td>
            <td><?= e($r['numero_documento']) ?></td>
            <td><?= e($r['categoria']) ?></td>
            <td><?= e($r['tipo_novedad']) ?></td>
            <td><?= status_pill($r['estado_novedad']) ?></td>
          </tr>
        <?php endforeach; ?></tbody></table></div>
      <?php endif; ?>
    </div>
    <div class="card"><h3>Últimas novedades</h3><div class="table-wrap"><table class="sortable"><thead><tr><th>Fecha</th><th>Empresa</th><th>Colaborador</th><th>Categoría</th><th>Tipo</th><th>Estado</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e(dt($r['fecha_registro'])) ?></td><td><?= e($r['company_name']) ?></td><td><?= e($r['nombre_completo']) ?></td><td><?= e($r['categoria']) ?></td><td><?= e($r['tipo_novedad']) ?></td><td><?= status_pill($r['estado_novedad']) ?></td></tr><?php endforeach; ?></tbody></table></div></div>
    <?php footer_html(); }

function companies(){ require_login(); if(!is_super()){ flash('err','Solo el superadministrador puede administrar empresas.'); header('Location: '.base_url('index.php?page=dashboard')); exit; } $rows=db()->query('SELECT * FROM companies ORDER BY nombre')->fetchAll(); header_html('Empresas'); ?><div class="card"><div class="actions"><a class="btn ok" href="<?= e(base_url('index.php?page=company_form')) ?>">Nueva empresa</a></div></div><div class="card"><div class="table-wrap"><table class="sortable"><thead><tr><th>Logo</th><th>Empresa</th><th>NIT</th><th>Ciudad</th><th>Contacto</th><th>Estado</th><th class="no-sort no-print">Acciones</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?php if(!empty($r['logo_ruta'])): ?><img class="company-logo-mini" src="<?= e(public_file_url($r['logo_ruta'])) ?>"><?php else: ?><span class="muted">Sin logo</span><?php endif; ?></td><td><?= e($r['nombre']) ?></td><td><?= e($r['nit']) ?></td><td><?= e($r['ciudad']) ?></td><td><?= e($r['contacto']) ?></td><td><?= status_pill($r['estado']) ?></td><td class="no-print"><a href="<?= e(base_url('index.php?page=company_form&id='.$r['id'])) ?>">Editar</a> <?= delete_button('company',$r['id'],'index.php?page=companies') ?></td></tr><?php endforeach; ?></tbody></table></div></div><?php footer_html(); }
function upload_company_logo($field,$company_id){ if(empty($_FILES[$field]) || $_FILES[$field]['error']===UPLOAD_ERR_NO_FILE) return [null,null]; $f=$_FILES[$field]; if($f['error']!==UPLOAD_ERR_OK) throw new RuntimeException('No fue posible cargar el logo.'); if($f['size']>3*1024*1024) throw new RuntimeException('El logo supera 3 MB.'); $allowed=['image/jpeg','image/png','image/webp','image/gif']; $mime=mime_content_type($f['tmp_name']); if(!in_array($mime,$allowed,true)) throw new RuntimeException('El logo debe ser JPG, PNG, WEBP o GIF.'); $ext=strtolower(pathinfo($f['name'],PATHINFO_EXTENSION)) ?: 'png'; $safe='logo_'.bin2hex(random_bytes(8)).'.'.$ext; $dir=rtrim(cfg('upload_dir'),'/').'/company_'.$company_id.'/logos'; if(!is_dir($dir)) mkdir($dir,0775,true); $dest=$dir.'/'.$safe; move_uploaded_file($f['tmp_name'],$dest); return [$f['name'], str_replace(rtrim(__DIR__,'/').'/', '', $dest)]; }
function company_form(){
    require_login(); if(!is_super()) exit('No autorizado');
    try{ if(!column_exists('companies','whatsapp_group_url')) db()->exec("ALTER TABLE companies ADD COLUMN whatsapp_group_url VARCHAR(500) NULL AFTER logo_ruta"); }catch(Throwable $e){}
    $id=(int)($_GET['id']??0); $r=['nombre'=>'','nit'=>'','ciudad'=>'','direccion'=>'','contacto'=>'','correo_contacto'=>'','telefono_contacto'=>'','whatsapp_group_url'=>'','estado'=>'Activa','logo_ruta'=>''];
    if($id){ $st=db()->prepare('SELECT * FROM companies WHERE id=?'); $st->execute([$id]); $r=$st->fetch() ?: $r; }
    if($_SERVER['REQUEST_METHOD']==='POST'){
        check_csrf(); $group=trim($_POST['whatsapp_group_url']??''); if($group!=='' && !preg_match('#^https?://#i',$group)) $group='https://'.$group;
        $data=[trim($_POST['nombre']),trim($_POST['nit']),trim($_POST['ciudad']),trim($_POST['direccion']),trim($_POST['contacto']),trim($_POST['correo_contacto']),trim($_POST['telefono_contacto']),$group?:null,$_POST['estado']];
        if($id){ $data[]=$id; db()->prepare('UPDATE companies SET nombre=?,nit=?,ciudad=?,direccion=?,contacto=?,correo_contacto=?,telefono_contacto=?,whatsapp_group_url=?,estado=? WHERE id=?')->execute($data); }
        else { db()->prepare('INSERT INTO companies(nombre,nit,ciudad,direccion,contacto,correo_contacto,telefono_contacto,whatsapp_group_url,estado) VALUES(?,?,?,?,?,?,?,?,?)')->execute($data); $id=db()->lastInsertId(); }
        try{ [$n,$rel]=upload_company_logo('logo_empresa',$id); if($n) db()->prepare('UPDATE companies SET logo_nombre=?, logo_ruta=? WHERE id=?')->execute([$n,$rel,$id]); }catch(Throwable $e){ flash('err',$e->getMessage()); }
        flash('ok','Empresa guardada.'); header('Location: '.base_url('index.php?page=companies')); exit;
    }
    header_html($id?'Editar empresa':'Nueva empresa'); ?>
    <div class="card"><form method="post" enctype="multipart/form-data"><?= csrf_field() ?><div class="grid cols-2">
      <div class="field"><label class="required">Nombre</label><input name="nombre" value="<?= e($r['nombre']) ?>" required></div>
      <div class="field"><label>NIT</label><input name="nit" value="<?= e($r['nit']) ?>"></div>
      <div class="field"><label>Ciudad</label><input name="ciudad" value="<?= e($r['ciudad']) ?>"></div>
      <div class="field"><label>Dirección</label><input name="direccion" value="<?= e($r['direccion'] ?? '') ?>"></div>
      <div class="field"><label>Contacto</label><input name="contacto" value="<?= e($r['contacto']) ?>"></div>
      <div class="field"><label>Correo</label><input name="correo_contacto" value="<?= e($r['correo_contacto']) ?>"></div>
      <div class="field"><label>Teléfono</label><input name="telefono_contacto" value="<?= e($r['telefono_contacto']) ?>"></div>
      <div class="field"><label>Enlace del grupo de WhatsApp Business</label><input name="whatsapp_group_url" value="<?= e($r['whatsapp_group_url']??'') ?>" placeholder="https://chat.whatsapp.com/..."><div class="hint">Este enlace se utiliza para abrir el grupo al compartir conceptos e informes de la empresa.</div></div>
      <div class="field"><label>Estado</label><select name="estado"><option <?= $r['estado']==='Activa'?'selected':'' ?>>Activa</option><option <?= $r['estado']==='Inactiva'?'selected':'' ?>>Inactiva</option></select></div>
      <div class="field"><label>Logo empresa cliente</label><input type="file" name="logo_empresa" accept="image/*"><?php if(!empty($r['logo_ruta'])): ?><div class="logo-preview"><img src="<?= e(public_file_url($r['logo_ruta'])) ?>"></div><?php endif; ?></div>
    </div><button class="btn">Guardar</button> <a class="btn secondary" href="<?= e(base_url('index.php?page=companies')) ?>">Cancelar</a></form></div><?php footer_html(); }


function collaborator_photo_url($row){
    return !empty($row['foto_ruta']) ? public_file_url($row['foto_ruta']) : '';
}
function collaborator_initials($name){
    $name=trim((string)$name); if($name==='') return 'C';
    $parts=preg_split('/\s+/', $name); $ini='';
    foreach($parts as $p){ if($p!==''){ $ini.=mb_strtoupper(mb_substr($p,0,1,'UTF-8'),'UTF-8'); } if(mb_strlen($ini,'UTF-8')>=2) break; }
    return $ini ?: 'C';
}
function collaborator_photo_dir($company_id){
    $dir=rtrim(cfg('upload_dir'),'/').'/company_'.$company_id.'/collaborators';
    if(!is_dir($dir)) mkdir($dir,0775,true);
    return $dir;
}
function save_collaborator_photo_data_url($dataUrl,$company_id,$collaborator_id){
    $dataUrl=trim((string)$dataUrl);
    if($dataUrl==='') return [null,null];
    if(!preg_match('/^data:image\/(png|jpeg|jpg|webp);base64,(.+)$/', $dataUrl, $m)){
        throw new RuntimeException('La foto recortada no tiene un formato válido.');
    }
    $bin=base64_decode($m[2], true);
    if($bin===false) throw new RuntimeException('No fue posible procesar la foto recortada.');
    if(strlen($bin)>4*1024*1024) throw new RuntimeException('La foto recortada supera 4 MB.');
    $ext=$m[1]==='jpeg'?'jpg':$m[1];
    $safe='foto_colaborador_'.$collaborator_id.'_recortada_'.bin2hex(random_bytes(8)).'.'.$ext;
    $dir=collaborator_photo_dir($company_id);
    $dest=$dir.'/'.$safe;
    file_put_contents($dest,$bin);
    return ['foto_colaborador_recortada.'.$ext, str_replace(rtrim(__DIR__,'/').'/', '', $dest)];
}
function upload_collaborator_photo($field,$company_id,$collaborator_id){
    if(empty($_FILES[$field]) || $_FILES[$field]['error']===UPLOAD_ERR_NO_FILE) return [null,null];
    $f=$_FILES[$field];
    if($f['error']!==UPLOAD_ERR_OK) throw new RuntimeException('No fue posible cargar la foto del colaborador.');
    if($f['size']>4*1024*1024) throw new RuntimeException('La foto supera 4 MB.');
    $allowed=['image/jpeg','image/png','image/webp','image/gif'];
    $mime=mime_content_type($f['tmp_name']);
    if(!in_array($mime,$allowed,true)) throw new RuntimeException('La foto debe ser JPG, PNG, WEBP o GIF.');
    $ext=strtolower(pathinfo($f['name'],PATHINFO_EXTENSION)) ?: 'jpg';
    $safe='foto_colaborador_'.$collaborator_id.'_'.bin2hex(random_bytes(8)).'.'.$ext;
    $dir=collaborator_photo_dir($company_id);
    $dest=$dir.'/'.$safe;
    move_uploaded_file($f['tmp_name'],$dest);
    return [$f['name'], str_replace(rtrim(__DIR__,'/').'/', '', $dest)];
}


function collaborator_validation_statuses(){ return ['Pendiente','Favorable','Favorable con observación','No favorable','No realizado']; }
function collaborator_acceptance_statuses(){ return ['Pendiente','Aceptada','Rechazada','No aplica']; }
function collaborator_security_concept_to_result($concepto){
    $c=mb_strtoupper((string)$concepto,'UTF-8');
    if(strpos($c,'NO FAVORABLE')!==false) return 'No favorable';
    if(strpos($c,'OBSERV')!==false) return 'Favorable con observación';
    if(strpos($c,'FAVORABLE')!==false) return 'Favorable';
    return $concepto ?: 'Pendiente';
}
function collaborator_support_dir($company_id,$collaborator_id){
    $dir=rtrim(cfg('upload_dir'),'/').'/company_'.$company_id.'/collaborators/'.$collaborator_id.'/validaciones';
    if(!is_dir($dir)) mkdir($dir,0775,true);
    return $dir;
}
function upload_collaborator_support_file($field,$company_id,$collaborator_id,$prefix){
    if(empty($_FILES[$field]) || $_FILES[$field]['error']===UPLOAD_ERR_NO_FILE) return [null,null];
    $f=$_FILES[$field];
    if($f['error']!==UPLOAD_ERR_OK) throw new RuntimeException('No fue posible cargar el soporte '.str_replace('_',' ',$field).'.');
    if($f['size']>8*1024*1024) throw new RuntimeException('El soporte supera 8 MB.');
    $allowedExt=['pdf','jpg','jpeg','png','webp','gif','doc','docx'];
    $ext=strtolower(pathinfo($f['name'],PATHINFO_EXTENSION));
    if(!in_array($ext,$allowedExt,true)) throw new RuntimeException('El soporte debe ser PDF, imagen o Word.');
    $safe=$prefix.'_'.$collaborator_id.'_'.date('Ymd_His').'_'.bin2hex(random_bytes(5)).'.'.$ext;
    $dir=collaborator_support_dir($company_id,$collaborator_id);
    $dest=$dir.'/'.$safe;
    move_uploaded_file($f['tmp_name'],$dest);
    return [$f['name'], str_replace(rtrim(__DIR__,'/').'/', '', $dest)];
}
function collaborator_latest_security_study($collaborator_id,$numero_documento,$company_id){
    try{
        $sql='SELECT * FROM security_studies WHERE company_id=? AND (collaborator_id=? OR numero_documento=?) ORDER BY fecha_estudio DESC, id DESC LIMIT 1';
        $st=db()->prepare($sql); $st->execute([(int)$company_id,(int)$collaborator_id,(string)$numero_documento]);
        $r=$st->fetch(); return $r ?: null;
    }catch(Throwable $e){ return null; }
}
function collaborator_security_study_by_id($study_id){
    if(!$study_id) return null;
    try{
        $st=db()->prepare('SELECT * FROM security_studies WHERE id=? LIMIT 1');
        $st->execute([(int)$study_id]);
        $r=$st->fetch();
        return ($r && can_read_company($r['company_id'])) ? $r : null;
    }catch(Throwable $e){ return null; }
}
function collaborator_security_studies_options($company_id=null,$selected=null,$numero_documento=''){
    try{
        $u=current_user();
        $sql='SELECT id,company_id,fecha_estudio,nombre_consulta,numero_documento,concepto,estado,documento_generado_ruta FROM security_studies WHERE 1=1';
        $params=[];
        if($u && user_restricts_company()){ $sql.=' AND company_id=?'; $params[]=$u['company_id']; }
        elseif($company_id){ $sql.=' AND company_id=?'; $params[]=$company_id; }
        if($numero_documento){ $sql.=' AND (numero_documento=? OR numero_documento LIKE ?)'; $params[]=$numero_documento; $params[]='%'.$numero_documento.'%'; }
        $sql.=' ORDER BY fecha_estudio DESC, id DESC LIMIT 250';
        $st=db()->prepare($sql); $st->execute($params);
        $html='<option value="">No enlazar / usar soporte cargado</option>';
        foreach($st->fetchAll() as $r){
            $sel=((string)$selected===(string)$r['id'])?' selected':'';
            $doc=!empty($r['documento_generado_ruta'])?' · Documento generado':' · Sin documento generado';
            $label=dt($r['fecha_estudio']).' · '.$r['numero_documento'].' · '.$r['nombre_consulta'].' · '.$r['concepto'].$doc;
            $html.='<option data-company="'.e($r['company_id']).'" value="'.e($r['id']).'"'.$sel.'>'.e($label).'</option>';
        }
        return $html;
    }catch(Throwable $e){ return '<option value="">No disponible</option>'; }
}
function collaborator_security_study_download_link($study,$label='Abrir estudio generado'){
    if(!$study) return '<span class="muted">Sin estudio enlazado</span>';
    if(!empty($study['documento_generado_ruta'])) return collaborator_file_link($study['documento_generado_ruta'],$label);
    return '<a class="btn secondary small" target="_blank" href="'.e(base_url('security_studies.php?action=download&id='.$study['id'])).'">Generar / abrir estudio</a>';
}
function collaborator_support_image_html($path,$alt='Soporte', $class='support-image-preview'){
    if(!$path) return '';
    $url=public_file_url($path);
    if(!collaborator_is_image_path($path)) return collaborator_file_link($path,'Abrir soporte');
    return '<a class="'.e($class).'" target="_blank" href="'.e($url).'"><img src="'.e($url).'" alt="'.e($alt).'"><span>Click para ampliar</span></a>';
}
function collaborator_is_image_path($path){ return (bool)preg_match('/\.(jpg|jpeg|png|webp|gif)$/i',(string)$path); }
function collaborator_file_link($path,$label='Ver soporte'){
    if(!$path) return '<span class="muted">Sin soporte</span>';
    return '<a class="btn secondary small" target="_blank" href="'.e(public_file_url($path)).'">'.e($label).'</a>';
}
function validation_status_pill($status){
    $s=trim((string)$status); if($s==='') $s='Pendiente';
    $u=mb_strtoupper($s,'UTF-8');
    $class='warn';
    if(strpos($u,'NO FAVORABLE')!==false || strpos($u,'RECHAZ')!==false) $class='red';
    elseif(strpos($u,'FAVORABLE')!==false || strpos($u,'ACEPT')!==false) $class='ok';
    elseif(strpos($u,'NO REALIZADO')!==false || strpos($u,'NO APLICA')!==false) $class='gray';
    return '<span class="pill '.$class.'">'.e($s).'</span>';
}

function collaborators(){ require_login(); $u=current_user(); $company=(int)($_GET['company_id']??0); $tipo_doc=$_GET['tipo_documento']??''; $estado=$_GET['estado']??''; $tipo_contrato=$_GET['tipo_vinculacion']??''; $salmod=$_GET['salario_modalidad']??''; $q=trim($_GET['q']??''); $sql='SELECT c.*, co.nombre company_name FROM collaborators c JOIN companies co ON co.id=c.company_id WHERE 1=1'; $params=[]; if(user_restricts_company()){ $sql.=' AND c.company_id=?'; $params[]=$u['company_id']; } elseif($company){ $sql.=' AND c.company_id=?'; $params[]=$company; } if($tipo_doc){ $sql.=' AND c.tipo_documento=?'; $params[]=$tipo_doc; } if($estado){ $sql.=' AND c.estado=?'; $params[]=$estado; } if($tipo_contrato){ $sql.=' AND c.tipo_vinculacion=?'; $params[]=$tipo_contrato; } if($salmod){ $sql.=' AND c.salario_modalidad=?'; $params[]=$salmod; } if($q){ $sql.=' AND (c.nombre_completo LIKE ? OR c.numero_documento LIKE ? OR c.cargo LIKE ? OR c.area LIKE ?)'; array_push($params,"%$q%","%$q%","%$q%","%$q%"); } $sql.=' ORDER BY FIELD(c.estado,"Prospecto","En selección","Activo","Contratado","Suspendido","Retirado","Inactivo","Candidato no seleccionado"), c.nombre_completo LIMIT 3000'; $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll(); header_html('Colaboradores'); ?><div class="sticky-controls no-print"><div class="card"><form method="get" class="filters"><input type="hidden" name="page" value="collaborators"><div><label>Empresa</label><select name="company_id"><option value="">Todas</option><?= company_options($company) ?></select></div><div><label>Tipo de ID</label><select name="tipo_documento"><?= distinct_options('collaborators','tipo_documento',$tipo_doc,'Todos') ?></select></div><div><label>Estado</label><select name="estado"><?= option_list(estados_colaborador(),$estado,'Todos') ?></select></div><div><label>Tipo de contrato</label><select name="tipo_vinculacion"><?= option_list(tipos_contrato(),$tipo_contrato,'Todos') ?></select></div><div><label>Salario mod.</label><select name="salario_modalidad"><?= option_list(salario_mods(),$salmod,'Todos') ?></select></div><div><label>Búsqueda</label><input name="q" value="<?= e($q) ?>" placeholder="Nombre, documento, cargo, área"></div><div><label>&nbsp;</label><button class="btn">Filtrar</button></div></form></div><div class="card compact-card"><div class="actions"><a class="btn ok" href="<?= e(base_url('index.php?page=collaborator_form')) ?>">Nuevo colaborador</a><a class="btn secondary" href="<?= e(base_url('index.php?page=collaborator_form&prospecto=1')) ?>">Nuevo prospecto</a><button class="btn secondary" onclick="copyTable('tabla-colaboradores')">Copiar tabla</button></div><div class="hint">Use <strong>Nuevo prospecto</strong> para registrar candidatos preliminares. Cuando supere selección, edítelo y cambie el estado a Activo o Contratado.</div></div></div><div class="card results-card"><div class="table-wrap results-table-wrap"><table id="tabla-colaboradores" class="sortable"><thead><tr><th>Nombre</th><th>Tipo de ID</th><th>Número de ID</th><th>Expedición</th><th>Empresa</th><th>Área</th><th>Cargo</th><th>Contrato</th><th>Ingreso</th><th>Retiro</th><th>Estado</th><th>Salario mod.</th><th>Salario</th><th>EPS</th><th>AFP</th><th>ARL</th><th>CCF</th><th>Cesantías</th><th class="no-sort no-print">Acciones</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr class="<?= collaborator_is_preliminar_estado($r['estado'])?'row-prospect':'' ?>"><td><a href="<?= e(base_url('index.php?page=collaborator_view&id='.$r['id'])) ?>"><?= e($r['nombre_completo']) ?></a></td><td><?= e($r['tipo_documento']) ?></td><td><?= e($r['numero_documento']) ?></td><td><?= e(dt($r['fecha_expedicion_documento'] ?? '')) ?></td><td><?= e($r['company_name']) ?></td><td><?= e($r['area']) ?></td><td><?= e($r['cargo']) ?></td><td><?= e($r['tipo_vinculacion']) ?></td><td><?= e(dt($r['fecha_ingreso'])) ?></td><td><?= e(dt($r['fecha_retiro'])) ?></td><td><?= status_pill($r['estado']) ?></td><td><?= e($r['salario_modalidad'] ?? '') ?></td><td><?= e(money($r['salario_base'])) ?></td><td><?= e($r['eps']) ?></td><td><?= e($r['afp']) ?></td><td><?= e($r['arl']) ?></td><td><?= e($r['caja_compensacion']) ?></td><td><?= e($r['fondo_cesantias']) ?></td><td class="no-print"><a href="<?= e(base_url('index.php?page=collaborator_view&id='.$r['id'])) ?>">Ver</a> · <a href="<?= e(base_url('index.php?page=collaborator_form&id='.$r['id'])) ?>">Editar</a><?php if(collaborator_is_preliminar_estado($r['estado'])): ?> · <a class="convert-link" href="<?= e(base_url('index.php?page=collaborator_form&id='.$r['id'].'&convertir=1')) ?>">Completar contratación</a><?php else: ?> · <a href="<?= e(base_url('contracts.php?collaborator_id='.$r['id'])) ?>">Contrato</a><?php endif; ?> <?= delete_button('collaborator',$r['id'],'index.php?page=collaborators') ?></td></tr><?php endforeach; ?></tbody></table></div></div><?php footer_html(); }
function collaborator_find_existing_by_document($company_id,$numero_documento){
    $numero_documento=trim((string)$numero_documento);
    if(!$company_id || $numero_documento==='') return null;
    $st=db()->prepare('SELECT * FROM collaborators WHERE company_id=? AND numero_documento=? ORDER BY id DESC LIMIT 1');
    $st->execute([(int)$company_id,$numero_documento]);
    $r=$st->fetch(); return $r ?: null;
}
function collaborator_column_metadata(){
    static $meta=null; if($meta!==null) return $meta;
    $st=db()->query("SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,EXTRA FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='collaborators'");
    $meta=[]; foreach($st->fetchAll() as $row){ $meta[$row['COLUMN_NAME']]=$row; }
    return $meta;
}
function collaborator_available_columns(){
    return array_fill_keys(array_keys(collaborator_column_metadata()),true);
}
function collaborator_normalize_db_value($column,$value,$columnMeta,$isInsert,&$include){
    $include=true;
    $type=strtolower((string)($columnMeta['DATA_TYPE'] ?? ''));
    $nullable=(($columnMeta['IS_NULLABLE'] ?? '')==='YES');
    $default=$columnMeta['COLUMN_DEFAULT'] ?? null;
    $blank=($value===null || (is_string($value) && trim($value)===''));
    $numeric=['tinyint','smallint','mediumint','int','integer','bigint','decimal','numeric','float','double','real','bit'];
    $temporal=['date','datetime','timestamp','time','year'];

    if($blank && (in_array($type,$numeric,true) || in_array($type,$temporal,true))){
        if($nullable) return null;
        if($default!==null){
            if($isInsert){ $include=false; return null; }
            return $default;
        }
        if(in_array($type,$numeric,true)) return 0;
        // Una fecha vacía nunca debe convertirse en cadena vacía. Si la columna
        // aún no admite NULL, la migración de endurecimiento la corrige.
        return null;
    }
    if(in_array($type,$numeric,true) && !$blank){
        if(is_numeric($value)) return $value;
        $normalized=normalize_money($value);
        return $normalized!==null ? $normalized : ($nullable ? null : 0);
    }
    return is_string($value) ? trim($value) : $value;
}
function collaborator_save_safely($data,$id,$user_id){
    $meta=collaborator_column_metadata();
    $isInsert=!$id;
    if($id && isset($meta['updated_by'])) $data['updated_by']=$user_id;
    if($isInsert && isset($meta['created_by'])) $data['created_by']=$user_id;

    $filtered=[];
    foreach($data as $k=>$v){
        if(!isset($meta[$k])) continue;
        $include=true;
        $normalized=collaborator_normalize_db_value($k,$v,$meta[$k],$isInsert,$include);
        if($include) $filtered[$k]=$normalized;
    }
    if(!$filtered) throw new RuntimeException('No hay datos válidos para guardar el registro.');

    if($id){
        $sets=[]; $vals=[];
        foreach($filtered as $k=>$v){ $sets[]="`$k`=?"; $vals[]=$v; }
        $vals[]=(int)$id;
        db()->prepare('UPDATE collaborators SET '.implode(',',$sets).' WHERE id=?')->execute($vals);
        return (int)$id;
    }
    $names=array_keys($filtered); $vals=array_values($filtered);
    $sql='INSERT INTO collaborators(`'.implode('`,`',$names).'`) VALUES('.implode(',',array_fill(0,count($names),'?')).')';
    db()->prepare($sql)->execute($vals);
    return (int)db()->lastInsertId();
}
function collaborator_form(){
    require_write();
    $u=current_user();
    $id=(int)($_GET['id']??0);
    $prospectRequested=(($_GET['prospecto'] ?? '')==='1');
    $convertRequested=(($_GET['convertir'] ?? '')==='1');
    $r=['company_id'=>can_manage_all_companies()?'':($u['company_id']??''),'nombre_completo'=>'','tipo_documento'=>'CC','numero_documento'=>'','fecha_expedicion_documento'=>'','foto_nombre'=>'','foto_ruta'=>'','area'=>'','cargo'=>'','direccion'=>'','telefono'=>'','correo_electronico'=>'','tipo_vinculacion'=>'Laboral','fecha_ingreso'=>'','fecha_retiro'=>'','estado'=>$prospectRequested?'Prospecto':'Activo','salario_modalidad'=>'Fijo','salario_base'=>'','eps'=>'','afp'=>'','arl'=>'','caja_compensacion'=>'','fondo_cesantias'=>'','estudio_seguridad_id'=>'','estudio_seguridad_resultado'=>'Pendiente','estudio_seguridad_fecha'=>'','estudio_seguridad_nombre'=>'','estudio_seguridad_ruta'=>'','estudio_seguridad_observacion'=>'','aceptacion_condiciones_estado'=>'Pendiente','aceptacion_condiciones_fecha'=>'','aceptacion_condiciones_nombre'=>'','aceptacion_condiciones_ruta'=>'','aceptacion_condiciones_observacion'=>''];
    if($id){
        $st=db()->prepare('SELECT * FROM collaborators WHERE id=?'); $st->execute([$id]);
        $r=$st->fetch() ?: $r; if(!can_read_company($r['company_id'])) exit('No autorizado');
    }
    $oldEstado=$r['estado'] ?? '';
    $isPreliminary=collaborator_is_preliminar_estado($r['estado'] ?? '');
    $simpleMode=(!$id && $prospectRequested) || ($id && $isPreliminary && !$convertRequested);

    if($_SERVER['REQUEST_METHOD']==='POST'){
        check_csrf();
        $submitAction=$_POST['submit_action'] ?? 'save';
        $simplePost=(($_POST['registro_modo'] ?? '')==='prospecto');
        $company_id=can_manage_all_companies()?(int)($_POST['company_id'] ?? 0):(int)($u['company_id'] ?: ($_POST['company_id'] ?? 0));
        if(!$company_id){ flash('err','Debe seleccionar una empresa.'); header('Location: '.base_url('index.php?page=collaborator_form'.($simplePost?'&prospecto=1':''))); exit; }
        $selectedSecurityStudyId=(int)($_POST['estudio_seguridad_id'] ?? 0);
        $post=function($key,$default=''){ return trim((string)($_POST[$key] ?? $default)); };
        $estado=$simplePost ? ($post('estado','Prospecto') ?: 'Prospecto') : $post('estado','Activo');
        if($simplePost && !in_array($estado,['Prospecto','En selección','Candidato no seleccionado'],true)) $estado='Prospecto';
        $saveData=[
            'company_id'=>$company_id,
            'nombre_completo'=>$post('nombre_completo'),
            'tipo_documento'=>$post('tipo_documento','CC'),
            'numero_documento'=>$post('numero_documento'),
            'fecha_expedicion_documento'=>dbdate($post('fecha_expedicion_documento')),
            'direccion'=>$post('direccion'),
            'telefono'=>$post('telefono'),
            'correo_electronico'=>$post('correo_electronico'),
            'estado'=>$estado,
            'estudio_seguridad_resultado'=>$post('estudio_seguridad_resultado',$r['estudio_seguridad_resultado']??'Pendiente'),
            'estudio_seguridad_fecha'=>dbdate($post('estudio_seguridad_fecha',$r['estudio_seguridad_fecha']??'')),
            'estudio_seguridad_observacion'=>$post('estudio_seguridad_observacion',$r['estudio_seguridad_observacion']??'')
        ];
        // El prospecto solo guarda identificación, contacto, estado del proceso y
        // estudio de seguridad. Los datos laborales se agregan al completar la contratación.
        if(!$simplePost){
            $saveData += [
                'area'=>$post('area'),
                'cargo'=>$post('cargo'),
                'tipo_vinculacion'=>$post('tipo_vinculacion','Laboral'),
                'fecha_ingreso'=>dbdate($post('fecha_ingreso')),
                'fecha_retiro'=>dbdate($post('fecha_retiro')),
                'salario_modalidad'=>$post('salario_modalidad','Fijo'),
                'salario_base'=>normalize_money($post('salario_base')),
                'eps'=>$post('eps'),
                'afp'=>$post('afp'),
                'arl'=>$post('arl'),
                'caja_compensacion'=>$post('caja_compensacion'),
                'fondo_cesantias'=>$post('fondo_cesantias'),
                'aceptacion_condiciones_estado'=>$post('aceptacion_condiciones_estado','Pendiente'),
                'aceptacion_condiciones_fecha'=>dbdate($post('aceptacion_condiciones_fecha')),
                'aceptacion_condiciones_observacion'=>$post('aceptacion_condiciones_observacion')
            ];
        }
        if($post('nombre_completo')==='' || $post('numero_documento')==='' || $post('fecha_expedicion_documento')===''){
            flash('err','Nombre, número de identificación y fecha de expedición son obligatorios.');
            header('Location: '.base_url('index.php?page=collaborator_form'.($id?'&id='.$id:'').($simplePost?'&prospecto=1':''))); exit;
        }
        try{
            if(!$id){
                $existing=collaborator_find_existing_by_document($company_id,$post('numero_documento'));
                if($existing){
                    $id=(int)$existing['id'];
                    flash('warn','Ya existía un registro con ese documento. Se actualizó el prospecto existente y se continuará con su estudio de seguridad.');
                }
            }
            $id=collaborator_save_safely($saveData,$id,$u['id']);
        }catch(Throwable $e){
            flash('err','No fue posible guardar el prospecto o colaborador: '.$e->getMessage());
            $back='index.php?page=collaborator_form'.($id?'&id='.$id:'').($simplePost?'&prospecto=1':'');
            header('Location: '.base_url($back)); exit;
        }
        if($selectedSecurityStudyId){
            $study=collaborator_security_study_by_id($selectedSecurityStudyId);
            if($study && (int)$study['company_id']===(int)$company_id){
                $studyName=!empty($study['documento_generado_ruta']) ? basename($study['documento_generado_ruta']) : ('Estudio de seguridad - '.$study['nombre_consulta']);
                db()->prepare('UPDATE collaborators SET estudio_seguridad_id=?, estudio_seguridad_resultado=?, estudio_seguridad_fecha=?, estudio_seguridad_nombre=?, estudio_seguridad_ruta=?, estudio_seguridad_observacion=?, updated_by=? WHERE id=?')->execute([$study['id'], collaborator_security_concept_to_result($study['concepto'] ?? ''), $study['fecha_estudio'] ?? null, $studyName, $study['documento_generado_ruta'] ?? '', $study['observacion'] ?? '', $u['id'], $id]);
            }
        }
        if(!$simplePost && collaborator_is_preliminar_estado($oldEstado) && collaborator_can_generate_ingreso_from_estado($estado) && $post('fecha_ingreso') && !collaborator_has_ingreso_novelty($id)){
            create_auto_novelty($company_id,$id,'Ingreso','Ingreso - '.$post('tipo_vinculacion','Laboral'),dbdate($post('fecha_ingreso')),null,'Ingreso generado al convertir prospecto en colaborador',null);
        } elseif(!$simplePost && !$oldEstado && $post('fecha_ingreso') && !collaborator_has_ingreso_novelty($id)){
            create_auto_novelty($company_id,$id,'Ingreso','Ingreso - '.$post('tipo_vinculacion','Laboral'),dbdate($post('fecha_ingreso')),null,'Ingreso generado desde creación de colaborador',null);
        }
        if(!$simplePost){
            try{
                [$fotoNombre,$fotoRuta]=[null,null];
                if(!empty($_POST['foto_colaborador_cropped'])){ [$fotoNombre,$fotoRuta]=save_collaborator_photo_data_url($_POST['foto_colaborador_cropped'],$company_id,$id); }
                if(!$fotoNombre){ [$fotoNombre,$fotoRuta]=upload_collaborator_photo('foto_colaborador',$company_id,$id); }
                if($fotoNombre){ db()->prepare('UPDATE collaborators SET foto_nombre=?, foto_ruta=?, updated_by=? WHERE id=?')->execute([$fotoNombre,$fotoRuta,$u['id'],$id]); }
                [$n2,$rel2]=upload_collaborator_support_file('aceptacion_condiciones_soporte',$company_id,$id,'aceptacion_condiciones');
                if($n2){ db()->prepare('UPDATE collaborators SET aceptacion_condiciones_nombre=?, aceptacion_condiciones_ruta=?, updated_by=? WHERE id=?')->execute([$n2,$rel2,$u['id'],$id]); }
            }catch(Throwable $e){ flash('err',$e->getMessage()); }
        }
        if($submitAction==='save_study'){
            flash('ok',$simplePost?'Prospecto guardado. Continúe con el estudio de seguridad; el estudio quedará vinculado automáticamente.':'Colaborador guardado. Continúe con el estudio de seguridad; el estudio quedará vinculado automáticamente.');
            $studyId=(int)($selectedSecurityStudyId ?: ($r['estudio_seguridad_id'] ?? 0));
            $studyUrl='security_studies.php?action=form&collaborator_id='.$id.'&return_mode=collaborator';
            if($studyId){ $studyUrl='security_studies.php?action=form&id='.$studyId.'&collaborator_id='.$id.'&return_mode=collaborator'; }
            header('Location: '.base_url($studyUrl)); exit;
        }
        flash('ok',$simplePost?'Prospecto guardado. Puede iniciar el estudio de seguridad o completar la contratación posteriormente.':'Colaborador guardado.');
        header('Location: '.base_url('index.php?page=collaborator_view&id='.$id)); exit;
    }

    $latestStudy=$id?collaborator_latest_security_study($id,$r['numero_documento'] ?? '',$r['company_id'] ?? 0):null;
    $securityStudyOptions=collaborator_security_studies_options($r['company_id'] ?? 0,$r['estudio_seguridad_id'] ?? '',$r['numero_documento'] ?? '');
    $linkedStudyId=(int)($r['estudio_seguridad_id'] ?? 0);
    if(!$linkedStudyId && $latestStudy){ $linkedStudyId=(int)$latestStudy['id']; }
    $securityStudyFormUrl=$id ? base_url('security_studies.php?action=form&'.($linkedStudyId?'id='.$linkedStudyId.'&':'').'collaborator_id='.$id.'&return_mode=collaborator') : '';
    $title=$simpleMode?($id?'Editar prospecto':'Nuevo prospecto'):($id?'Completar / editar colaborador':'Nuevo colaborador');
    header_html($title); ?>
<div class="card collaborator-mode-card no-print"><div class="collaborator-mode-actions"><a class="mode-option <?= $simpleMode?'active':'' ?>" href="<?= e(base_url('index.php?page=collaborator_form&prospecto=1'.($id?'&id='.$id:''))) ?>"><strong>1. Prospecto</strong><span>Registro preliminar para estudio de seguridad y selección.</span></a><a class="mode-option <?= !$simpleMode?'active':'' ?>" href="<?= e(base_url('index.php?page=collaborator_form'.($id?'&id='.$id.'&convertir=1':''))) ?>"><strong>2. Colaborador contratado</strong><span>Ingreso directo o complemento de la información del prospecto seleccionado.</span></a></div></div>
<div class="card"><form method="post" enctype="multipart/form-data"><?= csrf_field() ?><input type="hidden" name="registro_modo" value="<?= $simpleMode?'prospecto':'colaborador' ?>">
<?php if($simpleMode): ?>
    <div class="form-section-title">Información básica del prospecto</div>
    <div class="prospect-alert"><strong>Formulario preliminar:</strong> registra únicamente la información necesaria para identificación, contacto, selección y estudio de seguridad. Los datos laborales y de seguridad social se completan cuando el prospecto sea seleccionado.</div>
    <div class="grid cols-3 prospect-simple-grid">
      <div class="field"><label class="required">Empresa</label><select name="company_id" <?= user_restricts_company()?'disabled':'' ?>><?= company_options($r['company_id']) ?></select></div>
      <div class="field"><label class="required">Nombre completo</label><input name="nombre_completo" value="<?= e($r['nombre_completo']) ?>" required></div>
      <div class="field"><label class="required">Tipo de identificación</label><select name="tipo_documento"><?= option_list(['CC','CE','TI','PEP','PPT','Pasaporte','Otro'],$r['tipo_documento']) ?></select></div>
      <div class="field"><label class="required">Número de identificación</label><input name="numero_documento" value="<?= e($r['numero_documento']) ?>" required></div>
      <div class="field"><label class="required">Fecha de expedición</label><input type="date" name="fecha_expedicion_documento" value="<?= e($r['fecha_expedicion_documento'] ?? '') ?>" required></div>
      <div class="field"><label>Estado de selección</label><select name="estado"><?= option_list(['Prospecto','En selección','Candidato no seleccionado'],$r['estado'] ?: 'Prospecto') ?></select></div>
      <div class="field"><label>Teléfono / WhatsApp</label><input name="telefono" value="<?= e($r['telefono'] ?? '') ?>"></div>
      <div class="field"><label>Correo electrónico</label><input type="email" name="correo_electronico" value="<?= e($r['correo_electronico'] ?? '') ?>"></div>
      <div class="field"><label>Dirección / ciudad</label><input name="direccion" value="<?= e($r['direccion'] ?? '') ?>"></div>
    </div>
    <div class="form-section-title">Estudio de seguridad</div>
    <div class="grid cols-3">
      <div class="field"><label>Resultado preliminar</label><select name="estudio_seguridad_resultado"><?= option_list(collaborator_validation_statuses(),$r['estudio_seguridad_resultado'] ?? 'Pendiente') ?></select></div>
      <div class="field"><label>Fecha</label><input type="date" name="estudio_seguridad_fecha" value="<?= e($r['estudio_seguridad_fecha'] ?? '') ?>"></div>
      <div class="field"><label>Estudio generado en el módulo</label><select name="estudio_seguridad_id"><?= $securityStudyOptions ?></select></div>
      <div class="field span-all"><label>Observación</label><textarea name="estudio_seguridad_observacion"><?= e($r['estudio_seguridad_observacion'] ?? '') ?></textarea></div>
    </div>
    <div class="actions form-final-actions">
      <button class="btn ok" type="submit" name="submit_action" value="save">Guardar prospecto</button>
      <button class="btn" type="submit" name="submit_action" value="save_study">Guardar y generar estudio de seguridad</button>
      <?php if($id): ?>
        <a class="btn secondary" href="<?= e($securityStudyFormUrl) ?>"><?= $linkedStudyId?'Abrir estudio vinculado':'Generar estudio de seguridad' ?></a>
        <a class="btn secondary" href="<?= e(base_url('index.php?page=collaborator_form&id='.$id.'&convertir=1')) ?>">Completar contratación</a>
      <?php endif; ?>
      <a class="btn secondary" href="<?= e(base_url('index.php?page=collaborators')) ?>">Cancelar</a>
    </div>
<?php else: ?>
    <div class="form-section-title">Datos personales y de identificación</div>
    <div class="grid cols-3">
      <div class="field"><label class="required">Empresa</label><select name="company_id" <?= user_restricts_company()?'disabled':'' ?>><?= company_options($r['company_id']) ?></select></div><div class="field"><label class="required">Nombre completo</label><input name="nombre_completo" value="<?= e($r['nombre_completo']) ?>" required></div><div class="field"><label>Tipo ID</label><input name="tipo_documento" value="<?= e($r['tipo_documento']) ?>"></div><div class="field"><label class="required">Número ID</label><input name="numero_documento" value="<?= e($r['numero_documento']) ?>" required></div><div class="field"><label class="required">Fecha expedición</label><input type="date" name="fecha_expedicion_documento" value="<?= e($r['fecha_expedicion_documento'] ?? '') ?>" required></div><div class="field"><label>Dirección</label><input name="direccion" value="<?= e($r['direccion'] ?? '') ?>"></div><div class="field"><label>Teléfono</label><input name="telefono" value="<?= e($r['telefono'] ?? '') ?>"></div><div class="field"><label>Correo</label><input name="correo_electronico" value="<?= e($r['correo_electronico'] ?? '') ?>"></div>
      <div class="field photo-upload-field"><label>Foto del colaborador</label><input type="file" name="foto_colaborador" id="collabPhotoInput" accept="image/*"><input type="hidden" name="foto_colaborador_cropped" id="collabPhotoCropped"><div class="photo-crop-panel" id="photoCropPanel" hidden><div class="photo-crop-shell"><canvas id="photoCropCanvas" width="280" height="280"></canvas></div><div class="photo-crop-controls"><label>Zoom</label><input type="range" id="photoCropZoom" min="1" max="3" step="0.01" value="1"></div></div><?php if(!empty($r['foto_ruta'])): ?><div class="collab-photo-form-preview"><span>Foto actual</span><img src="<?= e(public_file_url($r['foto_ruta'])) ?>"></div><?php endif; ?></div>
    </div>
    <div class="form-section-title">Vinculación, cargo y compensación</div>
    <div class="grid cols-3"><div class="field"><label>Área</label><input name="area" value="<?= e($r['area']) ?>"></div><div class="field"><label>Cargo</label><input name="cargo" value="<?= e($r['cargo']) ?>"></div><div class="field"><label>Tipo contrato</label><select name="tipo_vinculacion"><?= option_list(tipos_contrato(),$r['tipo_vinculacion']) ?></select></div><div class="field"><label>Fecha ingreso</label><input type="date" name="fecha_ingreso" value="<?= e($r['fecha_ingreso']) ?>"></div><div class="field"><label>Fecha retiro</label><input type="date" name="fecha_retiro" value="<?= e($r['fecha_retiro']) ?>"></div><div class="field"><label>Estado</label><select name="estado"><?= option_list(estados_colaborador(),collaborator_is_preliminar_estado($r['estado'])?'Activo':$r['estado']) ?></select></div><div class="field"><label>Modalidad salarial</label><select name="salario_modalidad"><?= option_list(salario_mods(),$r['salario_modalidad'] ?? 'Fijo') ?></select></div><div class="field"><label>Salario</label><input name="salario_base" value="<?= e($r['salario_base']) ?>"></div></div>
    <div class="form-section-title">Seguridad social</div>
    <div class="grid cols-3"><div class="field"><label>EPS</label><input name="eps" value="<?= e($r['eps']) ?>"></div><div class="field"><label>AFP</label><input name="afp" value="<?= e($r['afp']) ?>"></div><div class="field"><label>ARL</label><input name="arl" value="<?= e($r['arl']) ?>"></div><div class="field"><label>CCF</label><input name="caja_compensacion" value="<?= e($r['caja_compensacion']) ?>"></div><div class="field"><label>Cesantías</label><input name="fondo_cesantias" value="<?= e($r['fondo_cesantias']) ?>"></div></div>
    <div class="form-section-title">Validaciones previas de vinculación</div>
    <div class="grid cols-2 validation-form-grid"><div class="validation-form-card"><h3>Estudio de seguridad</h3><div class="grid cols-2"><div class="field"><label>Resultado</label><select name="estudio_seguridad_resultado"><?= option_list(collaborator_validation_statuses(),$r['estudio_seguridad_resultado'] ?? 'Pendiente') ?></select></div><div class="field"><label>Fecha</label><input type="date" name="estudio_seguridad_fecha" value="<?= e($r['estudio_seguridad_fecha'] ?? '') ?>"></div></div><div class="field"><label>Enlazar estudio generado</label><select name="estudio_seguridad_id"><?= $securityStudyOptions ?></select></div><div class="field"><label>Observación</label><textarea name="estudio_seguridad_observacion"><?= e($r['estudio_seguridad_observacion'] ?? '') ?></textarea></div></div><div class="validation-form-card"><h3>Aceptación de condiciones</h3><div class="grid cols-2"><div class="field"><label>Resultado</label><select name="aceptacion_condiciones_estado"><?= option_list(collaborator_acceptance_statuses(),$r['aceptacion_condiciones_estado'] ?? 'Pendiente') ?></select></div><div class="field"><label>Fecha</label><input type="date" name="aceptacion_condiciones_fecha" value="<?= e($r['aceptacion_condiciones_fecha'] ?? '') ?>"></div></div><div class="field"><label>Soporte WhatsApp / PDF</label><input type="file" name="aceptacion_condiciones_soporte" accept=".pdf,image/*"></div><div class="field"><label>Observación</label><textarea name="aceptacion_condiciones_observacion"><?= e($r['aceptacion_condiciones_observacion'] ?? '') ?></textarea></div></div></div>
    <div class="actions form-final-actions">
      <button class="btn ok" type="submit" name="submit_action" value="save">Guardar colaborador</button>
      <button class="btn" type="submit" name="submit_action" value="save_study">Guardar y generar estudio de seguridad</button>
      <?php if($id): ?><a class="btn secondary" href="<?= e($securityStudyFormUrl) ?>"><?= $linkedStudyId?'Abrir estudio vinculado':'Generar estudio de seguridad' ?></a><?php endif; ?>
      <a class="btn secondary" href="<?= e(base_url('index.php?page=collaborators')) ?>">Cancelar</a>
    </div>
<?php endif; ?></form></div>
<script>
(function(){
  const input=document.getElementById('collabPhotoInput'), hidden=document.getElementById('collabPhotoCropped'), panel=document.getElementById('photoCropPanel'), canvas=document.getElementById('photoCropCanvas'), zoom=document.getElementById('photoCropZoom');
  if(!input||!hidden||!panel||!canvas||!zoom) return; const ctx=canvas.getContext('2d'); let img=null,scaleBase=1,offsetX=0,offsetY=0,dragging=false,lastX=0,lastY=0;
  function clamp(){if(!img)return;const z=parseFloat(zoom.value||'1'),dw=img.naturalWidth*scaleBase*z,dh=img.naturalHeight*scaleBase*z,maxX=Math.max(0,(dw-canvas.width)/2),maxY=Math.max(0,(dh-canvas.height)/2);offsetX=Math.max(-maxX,Math.min(maxX,offsetX));offsetY=Math.max(-maxY,Math.min(maxY,offsetY));}
  function draw(){if(!img)return;clamp();const z=parseFloat(zoom.value||'1'),dw=img.naturalWidth*scaleBase*z,dh=img.naturalHeight*scaleBase*z,x=(canvas.width-dw)/2+offsetX,y=(canvas.height-dh)/2+offsetY;ctx.clearRect(0,0,canvas.width,canvas.height);ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);ctx.drawImage(img,x,y,dw,dh);ctx.strokeStyle='#01495E';ctx.lineWidth=4;ctx.strokeRect(2,2,canvas.width-4,canvas.height-4);hidden.value=canvas.toDataURL('image/png');}
  function point(ev){const t=ev.touches&&ev.touches[0]?ev.touches[0]:ev;return{x:t.clientX,y:t.clientY};}
  input.addEventListener('change',()=>{const file=input.files&&input.files[0];if(!file)return;const reader=new FileReader();reader.onload=e=>{img=new Image();img.onload=()=>{scaleBase=Math.max(canvas.width/img.naturalWidth,canvas.height/img.naturalHeight);offsetX=offsetY=0;zoom.value='1';panel.hidden=false;draw();};img.src=e.target.result;};reader.readAsDataURL(file);}); zoom.addEventListener('input',draw);
  canvas.addEventListener('mousedown',ev=>{dragging=true;let p=point(ev);lastX=p.x;lastY=p.y;}); window.addEventListener('mouseup',()=>dragging=false); window.addEventListener('mousemove',ev=>{if(!dragging)return;let p=point(ev);offsetX+=p.x-lastX;offsetY+=p.y-lastY;lastX=p.x;lastY=p.y;draw();});
  canvas.addEventListener('touchstart',ev=>{dragging=true;let p=point(ev);lastX=p.x;lastY=p.y;},{passive:true});canvas.addEventListener('touchend',()=>dragging=false,{passive:true});canvas.addEventListener('touchmove',ev=>{if(!dragging)return;let p=point(ev);offsetX+=p.x-lastX;offsetY+=p.y-lastY;lastX=p.x;lastY=p.y;draw();},{passive:true});
})();
</script><?php footer_html(); }

function collaborator_view(){ require_login(); $id=(int)($_GET['id']??0);
    $companySelect='co.nombre company_name';
    $companySelect .= column_exists('companies','logo_ruta') ? ', co.logo_ruta company_logo' : ', NULL company_logo';
    $companySelect .= column_exists('companies','nit') ? ', co.nit company_nit' : ', NULL company_nit';
    $st=db()->prepare('SELECT c.*, '.$companySelect.' FROM collaborators c JOIN companies co ON co.id=c.company_id WHERE c.id=?'); $st->execute([$id]); $r=$st->fetch(); if(!$r || !can_read_company($r['company_id'])) exit('No autorizado');
    $st=db()->prepare('SELECT * FROM novelties WHERE collaborator_id=? ORDER BY COALESCE(fecha_inicio,fecha_registro) DESC, id DESC LIMIT 120'); $st->execute([$id]); $novs=$st->fetchAll();
    $latestStudy=collaborator_latest_security_study($id,$r['numero_documento'] ?? '',$r['company_id']);
    $linkedStudy=!empty($r['estudio_seguridad_id']) ? collaborator_security_study_by_id($r['estudio_seguridad_id']) : null;
    if(!$linkedStudy) $linkedStudy=$latestStudy;
    $photo=collaborator_photo_url($r); $clientLogo=company_logo_url($r['company_id']);
    $mandatosLogo=mandatos_logo_url();
    $securityStatus=$r['estudio_seguridad_resultado'] ?: ($linkedStudy['concepto'] ?? 'Pendiente');
    $securityDate=$r['estudio_seguridad_fecha'] ?: ($linkedStudy['fecha_estudio'] ?? '');
    $securityRoute=$r['estudio_seguridad_ruta'] ?: ($linkedStudy['documento_generado_ruta'] ?? '');
    $securityLink=$securityRoute ? collaborator_file_link($securityRoute,'Abrir estudio') : collaborator_security_study_download_link($linkedStudy,'Abrir estudio');
    $acceptStatus=$r['aceptacion_condiciones_estado'] ?: 'Pendiente';
    $acceptRoute=$r['aceptacion_condiciones_ruta'] ?? '';
    $printTitle='Ficha de colaborador - '.preg_replace('/[^A-Za-zÁÉÍÓÚÜÑáéíóúüñ0-9 _.-]+/u','', $r['nombre_completo']);
    $rowsIdent=[ ['Tipo de documento',$r['tipo_documento']], ['Número de documento',$r['numero_documento']], ['Fecha de expedición',dt($r['fecha_expedicion_documento'] ?? '')], ['Dirección',$r['direccion'] ?? ''], ['Teléfono',$r['telefono'] ?? ''], ['Correo electrónico',$r['correo_electronico'] ?? ''] ];
    $rowsVinc=[ ['Empresa',$r['company_name']], ['NIT',$r['company_nit'] ?? ''], ['Área',$r['area']], ['Cargo',$r['cargo']], ['Tipo de vinculación',$r['tipo_vinculacion']], ['Fecha de ingreso',dt($r['fecha_ingreso'])], ['Fecha de retiro',dt($r['fecha_retiro'])], ['Estado',$r['estado']] ];
    $rowsSeg=[ ['Modalidad salarial',$r['salario_modalidad'] ?? ''], ['Salario base',money($r['salario_base'])], ['EPS',$r['eps']], ['AFP',$r['afp']], ['ARL',$r['arl']], ['CCF',$r['caja_compensacion']], ['Fondo de cesantías',$r['fondo_cesantias']] ];
    header_html('Ficha de colaborador'); ?>
<div class="card no-print"><div class="actions"><a class="btn" href="<?= e(base_url('index.php?page=collaborator_form&id='.$id)) ?>">Editar</a><a class="btn secondary" href="<?= e(base_url('index.php?page=novelty_form&collaborator_id='.$id.'&company_id='.$r['company_id'])) ?>">Nueva novedad</a><a class="btn secondary" href="<?= e(base_url('contracts.php?collaborator_id='.$id.'&company_id='.$r['company_id'])) ?>">Generar contrato</a><a class="btn secondary" href="<?= e(base_url('index.php?page=liquidation_form&collaborator_id='.$id.'&company_id='.$r['company_id'])) ?>">Liquidación</a><a class="btn secondary" href="<?= e(base_url('security_studies.php?action=form&collaborator_id='.$id)) ?>">Estudio seguridad</a><?php if(!empty($r['telefono'])): ?><?= whatsapp_button_html('WhatsApp colaborador',$r['telefono'],'Buenos días '.$r['nombre_completo'].'. Compartimos enlace de su ficha: '.app_absolute_url($_SERVER['REQUEST_URI'] ?? '')) ?><?php endif; ?><label class="checkline print-toggle"><input type="checkbox" id="includeNoveltiesPrint" checked> Incluir novedades al imprimir</label><button class="btn" type="button" onclick="printCollaboratorFicha()">Imprimir ficha</button></div></div>
<div id="report-print-area" class="card collaborator-ficha-card ficha-print-sheet" data-print-title="<?= e($printTitle) ?>">
    <div class="ficha-doc-header"><div class="ficha-logo-box ficha-logo-mandatos"><?php if($mandatosLogo): ?><img src="<?= e($mandatosLogo) ?>" alt="Mandatos"><?php endif; ?></div><div class="ficha-title-box"><div>FICHA DE COLABORADOR</div><span><?= e($r['company_name']) ?></span><small>Generado: <?= e(date('d/m/Y H:i')) ?></small></div><div class="ficha-meta-box"><b>Código:</b> GE-MD-TH-01<br><b>Versión:</b> 001<br><b>Fecha:</b> <?= e(date('d/m/Y')) ?></div><div class="ficha-logo-box ficha-logo-client"><?php if($clientLogo): ?><img src="<?= e($clientLogo) ?>" alt="Logo empresa"><?php else: ?><strong><?= e($r['company_name']) ?></strong><?php endif; ?></div></div>
    <div class="ficha-body-layout">
        <aside class="ficha-profile-card"><div class="ficha-photo-box ficha-photo-large"><?php if($photo): ?><img src="<?= e($photo) ?>" alt="Foto colaborador"><?php else: ?><div class="ficha-photo-placeholder"><?= e(collaborator_initials($r['nombre_completo'])) ?></div><?php endif; ?></div><h2><?= e($r['nombre_completo']) ?></h2><div class="ficha-green-line"></div><div class="ficha-profile-item"><span>Documento</span><strong><?= e($r['numero_documento']) ?></strong></div><div class="ficha-profile-pills"><span><?= e($r['tipo_vinculacion']) ?></span><span><?= e($r['cargo']) ?></span><?= status_pill($r['estado']) ?></div></aside>
        <div class="ficha-details-stack"><section class="ficha-info-section ficha-block-ident"><h3>Datos de identificación y contacto</h3><table class="ficha-info-table ficha-two-col"><tbody><?php for($i=0;$i<count($rowsIdent);$i+=2): ?><tr><th><?= e($rowsIdent[$i][0]) ?></th><td><?= ficha_info_value_html($rowsIdent[$i][0],$rowsIdent[$i][1],$r) ?></td><?php if(isset($rowsIdent[$i+1])): ?><th><?= e($rowsIdent[$i+1][0]) ?></th><td><?= ficha_info_value_html($rowsIdent[$i+1][0],$rowsIdent[$i+1][1],$r) ?></td><?php else: ?><th></th><td></td><?php endif; ?></tr><?php endfor; ?></tbody></table></section>
        <section class="ficha-info-section ficha-block-vinc"><h3>Vinculación y cargo</h3><table class="ficha-info-table ficha-two-col"><tbody><?php for($i=0;$i<count($rowsVinc);$i+=2): ?><tr><th><?= e($rowsVinc[$i][0]) ?></th><td><?= e($rowsVinc[$i][1]) ?></td><?php if(isset($rowsVinc[$i+1])): ?><th><?= e($rowsVinc[$i+1][0]) ?></th><td><?= e($rowsVinc[$i+1][1]) ?></td><?php else: ?><th></th><td></td><?php endif; ?></tr><?php endfor; ?></tbody></table></section>
        <section class="ficha-info-section ficha-block-seg"><h3>Compensación y seguridad social</h3><table class="ficha-info-table ficha-two-col"><tbody><?php for($i=0;$i<count($rowsSeg);$i+=2): ?><tr><th><?= e($rowsSeg[$i][0]) ?></th><td><?= e($rowsSeg[$i][1]) ?></td><?php if(isset($rowsSeg[$i+1])): ?><th><?= e($rowsSeg[$i+1][0]) ?></th><td><?= e($rowsSeg[$i+1][1]) ?></td><?php else: ?><th></th><td></td><?php endif; ?></tr><?php endfor; ?></tbody></table></section></div>
    </div>
    <section class="ficha-info-section ficha-validaciones-section"><h3>Validaciones previas de vinculación</h3><div class="validation-cards-grid"><div class="validation-status-card"><div class="validation-card-head"><strong>Estudio de seguridad</strong><?= validation_status_pill($securityStatus) ?></div><table class="validation-mini-table"><tr><th>Fecha</th><td><?= e(dt($securityDate)) ?></td></tr><tr><th>Documento</th><td><?= $securityLink ?></td></tr><?php if(!empty($r['estudio_seguridad_observacion'])): ?><tr><th>Observación</th><td><?= e($r['estudio_seguridad_observacion']) ?></td></tr><?php endif; ?></table></div><div class="validation-status-card validation-acceptance-card"><div class="validation-card-head"><strong>Aceptación de condiciones</strong><?= validation_status_pill($acceptStatus) ?></div><table class="validation-mini-table"><tr><th>Fecha</th><td><?= e(dt($r['aceptacion_condiciones_fecha'] ?? '')) ?></td></tr><?php if(!$acceptRoute || !collaborator_is_image_path($acceptRoute)): ?><tr><th>Soporte</th><td><?= collaborator_file_link($acceptRoute,'Abrir soporte') ?></td></tr><?php endif; ?><?php if(!empty($r['aceptacion_condiciones_observacion'])): ?><tr><th>Observación</th><td><?= e($r['aceptacion_condiciones_observacion']) ?></td></tr><?php endif; ?></table><?php if($acceptRoute && collaborator_is_image_path($acceptRoute)): ?><div class="acceptance-inline-image"><div class="acceptance-inline-title">Soporte de aceptación / WhatsApp</div><?= collaborator_support_image_html($acceptRoute,'Soporte aceptación','acceptance-image-link') ?></div><?php endif; ?></div></div></section>
    <section class="ficha-novedades-section"><div class="ficha-section-title-row"><h3>Novedades del colaborador</h3><span><?= e(count($novs)) ?> registros</span></div><div class="table-wrap ficha-novedades-table"><table class="ficha-novedades-grid"><thead><tr><th>Fecha</th><th>Quincena</th><th>Categoría</th><th>Tipo</th><th>Inicio/Gestión</th><th>Terminación</th><th>Días</th><th>Valor</th><th>Estado</th><th>Descripción</th></tr></thead><tbody><?php foreach($novs as $n): ?><tr><td><?= e(dt($n['fecha_registro'])) ?></td><td><?= e($n['quincena']) ?></td><td><?= e($n['categoria']) ?></td><td><?= e($n['tipo_novedad']) ?></td><td><?= e(dt($n['fecha_inicio'] ?: $n['fecha_registro'])) ?></td><td><?= e($n['fecha_fin']?dt($n['fecha_fin']):'NA') ?></td><td><?= e($n['dias']) ?></td><td><?= e(money($n['valor'])) ?></td><td><?= status_pill($n['estado_novedad']) ?></td><td><?= e($n['descripcion']) ?></td></tr><?php endforeach; ?></tbody></table></div></section>
</div>
<script>
function printCollaboratorFicha(){ var cb=document.getElementById('includeNoveltiesPrint'); document.body.classList.toggle('hide-ficha-novedades-print', cb && !cb.checked); var area=document.getElementById('report-print-area'); var oldTitle=document.title; var suggested=(area && area.dataset.printTitle) ? area.dataset.printTitle : 'Ficha de colaborador'; document.title=suggested; setTimeout(function(){ window.print(); setTimeout(function(){ document.title=oldTitle; }, 1200); },50); }
</script><?php footer_html(); }

function novelty_form(){ require_write(); $u=current_user(); $id=(int)($_GET['id']??0); $prefColl=(int)($_GET['collaborator_id']??0); $prefCompany=(int)($_GET['company_id']??0); $r=['company_id'=>$prefCompany ?: ($u['company_id']??''),'collaborator_id'=>$prefColl,'fecha_registro'=>date('Y-m-d'),'fecha_inicio'=>'','fecha_fin'=>'','categoria'=>'Ingreso','tipo_novedad'=>'','dias'=>'','horas'=>'','valor'=>'','estado_novedad'=>'Registrada','impacto_legal'=>'','descripcion'=>'','origen'=>'Registro manual']; if($id){ $st=db()->prepare('SELECT * FROM novelties WHERE id=?'); $st->execute([$id]); $r=$st->fetch() ?: $r; if(!can_read_company($r['company_id'])) exit('No autorizado'); $r['categoria']=canonical_novelty_category($r['categoria']??'',$r['tipo_novedad']??'') ?: ($r['categoria']??'Ingreso'); } if($_SERVER['REQUEST_METHOD']==='POST'){ check_csrf(); $company_id=can_manage_all_companies()?(int)$_POST['company_id']:(int)($u['company_id'] ?: $_POST['company_id']); $coll=$_POST['collaborator_id']?(int)$_POST['collaborator_id']:null; $docText=null; $nameText=null; if($coll){ $st=db()->prepare('SELECT id,company_id,numero_documento,nombre_completo FROM collaborators WHERE id=?'); $st->execute([$coll]); $c=$st->fetch(); if(!$c || (int)$c['company_id'] !== $company_id){ flash('err','El colaborador seleccionado no corresponde a la empresa.'); header('Location: '.base_url('index.php?page=novelty_form')); exit; } $docText=$c['numero_documento']; $nameText=$c['nombre_completo']; } $fecha_registro=dbdate($_POST['fecha_registro']) ?: date('Y-m-d'); $fecha_inicio=dbdate($_POST['fecha_inicio']); $fecha_fin=dbdate($_POST['fecha_fin']); $quincena=calc_quincena($fecha_inicio ?: $fecha_registro); $tipoNovedad=trim($_POST['tipo_novedad']); $categoriaCanonica=canonical_novelty_category($_POST['categoria'],$tipoNovedad) ?: $_POST['categoria']; $vals=[$company_id,$coll,$docText,$nameText,$fecha_registro,$fecha_inicio,$fecha_fin,$quincena,$categoriaCanonica,$tipoNovedad,normalize_money($_POST['dias']),normalize_money($_POST['horas']),normalize_money($_POST['valor']),$_POST['estado_novedad'],trim($_POST['impacto_legal']),trim($_POST['descripcion']),trim($_POST['origen'])]; if($id){ $vals[]=$u['id']; $vals[]=$id; db()->prepare('UPDATE novelties SET company_id=?,collaborator_id=?,documento_text=?,colaborador_text=?,fecha_registro=?,fecha_inicio=?,fecha_fin=?,quincena=?,categoria=?,tipo_novedad=?,dias=?,horas=?,valor=?,estado_novedad=?,impacto_legal=?,descripcion=?,origen=?,updated_by=? WHERE id=?')->execute($vals); } else { $vals[]=$u['id']; db()->prepare('INSERT INTO novelties(company_id,collaborator_id,documento_text,colaborador_text,fecha_registro,fecha_inicio,fecha_fin,quincena,categoria,tipo_novedad,dias,horas,valor,estado_novedad,impacto_legal,descripcion,origen,created_by) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')->execute($vals); $id=db()->lastInsertId(); } flash('ok','Novedad guardada.'); header('Location: '.base_url('index.php?page=novelties')); exit; } $sql='SELECT id,company_id,nombre_completo,numero_documento FROM collaborators WHERE 1=1'; $params=[]; if(user_restricts_company()){ $sql.=' AND company_id=?'; $params[]=$u['company_id']; } $sql.=' ORDER BY numero_documento,nombre_completo LIMIT 5000'; $st=db()->prepare($sql); $st->execute($params); $colls=$st->fetchAll(); header_html($id?'Editar novedad':'Nueva novedad'); ?><div class="card"><form method="post"><?= csrf_field() ?><div class="grid cols-3"><div class="field"><label>Empresa</label><select id="companySelect" name="company_id" <?= user_restricts_company()?'disabled':'' ?>><?= company_options($r['company_id']) ?></select></div><div class="field"><label>Colaborador</label><select id="collaboratorSelect" class="combo-collaborator" name="collaborator_id"><option value="">Seleccione colaborador</option><?php foreach($colls as $c): ?><option data-company="<?= e($c['company_id']) ?>" value="<?= e($c['id']) ?>" <?= (string)$r['collaborator_id']===(string)$c['id']?'selected':'' ?>><?= e($c['numero_documento'].' - '.$c['nombre_completo']) ?></option><?php endforeach; ?></select></div><div class="field"><label>Fecha registro</label><input type="date" name="fecha_registro" value="<?= e($r['fecha_registro']) ?>"></div><div class="field"><label>Fecha inicio/gestión</label><input type="date" name="fecha_inicio" value="<?= e($r['fecha_inicio']) ?>"></div><div class="field"><label>Fecha terminación</label><input type="date" name="fecha_fin" value="<?= e($r['fecha_fin']) ?>"></div><div class="field"><label>Categoría</label><select name="categoria"><?= option_list(categories(),$r['categoria']) ?></select></div><div class="field"><label>Tipo</label><input name="tipo_novedad" value="<?= e($r['tipo_novedad']) ?>" required></div><div class="field"><label>Días</label><input name="dias" value="<?= e($r['dias']) ?>"></div><div class="field"><label>Horas</label><input name="horas" value="<?= e($r['horas']) ?>"></div><div class="field"><label>Valor</label><input name="valor" value="<?= e($r['valor']) ?>"></div><div class="field"><label>Estado</label><select name="estado_novedad"><?= option_list(['Registrada','En revisión','Cerrada','Anulada'],$r['estado_novedad']) ?></select></div><div class="field"><label>Origen</label><input name="origen" value="<?= e($r['origen']) ?>"></div></div><div class="field"><label>Impacto legal</label><textarea name="impacto_legal"><?= e($r['impacto_legal']) ?></textarea></div><div class="field"><label>Descripción</label><textarea name="descripcion"><?= e($r['descripcion']) ?></textarea></div><button class="btn">Guardar</button></form></div><?php footer_html(); }

function novelties(){
    require_login(); $u=current_user();
    $company=(int)($_GET['company_id']??0); $categoria=$_GET['categoria']??''; $coll=(int)($_GET['collaborator_id']??0); $q=trim($_GET['q']??'');
    $sql='SELECT '.novelty_select_columns().' FROM novelties n LEFT JOIN collaborators c ON c.id=n.collaborator_id JOIN companies co ON co.id=n.company_id WHERE 1=1'; $params=[];
    if(user_restricts_company()){ $sql.=' AND n.company_id=?'; $params[]=$u['company_id']; } elseif($company){ $sql.=' AND n.company_id=?'; $params[]=$company; }
    if($coll){ $sql.=' AND n.collaborator_id=?'; $params[]=$coll; } $sql.=category_filter_sql('n',$categoria,$params);
    if($q){ $sql.=' AND (n.tipo_novedad LIKE ? OR n.descripcion LIKE ? OR COALESCE(c.nombre_completo,n.colaborador_text) LIKE ? OR COALESCE(c.numero_documento,n.documento_text) LIKE ?)'; array_push($params,"%$q%","%$q%","%$q%","%$q%"); }
    $sql.=' ORDER BY COALESCE(n.fecha_inicio,n.fecha_registro) DESC, n.id DESC LIMIT 3000'; $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll();
    $events=[]; foreach($rows as $r){
        $base=$r['fecha_inicio'] ?: $r['fecha_registro'];
        if($base) $events[]=['date'=>$base,'kind'=>'Inicio / gestión','title'=>$r['categoria'].' · '.$r['tipo_novedad'],'name'=>$r['nombre_completo'],'company'=>$r['company_name'],'status'=>$r['estado_novedad'],'end'=>$r['fecha_fin']];
        if(!empty($r['fecha_fin'])) $events[]=['date'=>$r['fecha_fin'],'kind'=>'Terminación','title'=>$r['categoria'].' · '.$r['tipo_novedad'],'name'=>$r['nombre_completo'],'company'=>$r['company_name'],'status'=>$r['estado_novedad'],'end'=>$r['fecha_fin']];
    }
    $reportCompany=$company?get_company($company):report_company_for_header($rows,$company); $clientLogo=$reportCompany && !empty($reportCompany['logo_ruta'])?public_file_url($reportCompany['logo_ruta']):'';
    header_html('Novedades'); ?>
<div class="sticky-controls no-print"><div class="card"><form method="get" class="filters"><input type="hidden" name="page" value="novelties"><div><label>Empresa</label><select name="company_id"><option value="">Todas</option><?= company_options($company) ?></select></div><div><label>Colaborador</label><select class="combo-collaborator" name="collaborator_id"><?= collaborator_options($company,$coll) ?></select></div><div><label>Categoría</label><select name="categoria"><?= option_list(categories(),$categoria,'Todas') ?></select></div><div><label>Búsqueda</label><input name="q" value="<?= e($q) ?>" placeholder="Tipo, documento, nombre"></div><div><label>&nbsp;</label><button class="btn">Filtrar</button></div></form></div><div class="card compact-card"><div class="actions"><a class="btn ok" href="<?= e(base_url('index.php?page=novelty_form')) ?>">Nueva novedad</a><button type="button" class="btn secondary calendar-open-btn" onclick="openNoveltyCalendar()">Abrir calendario</button></div></div></div>
<div class="card"><div class="table-wrap results-table-wrap"><table class="sortable"><thead><tr><th>Fecha</th><th>Quincena</th><th>Empresa</th><th>Colaborador</th><th>Documento</th><th>Categoría</th><th>Tipo</th><th>Inicio/gestión</th><th>Terminación</th><th>Estado</th><th class="no-sort no-print">Acciones</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e(dt($r['fecha_registro'])) ?></td><td><?= e($r['quincena']) ?></td><td><?= e($r['company_name']) ?></td><td><?php if(!empty($r['collaborator_id'])): ?><a href="<?= e(base_url('index.php?page=collaborator_view&id='.$r['collaborator_id'])) ?>"><?= e($r['nombre_completo']) ?></a><?php else: ?><?= e($r['nombre_completo']) ?><?php endif; ?></td><td><?= e($r['numero_documento']) ?></td><td><?= e($r['categoria']) ?></td><td><?= e($r['tipo_novedad']) ?></td><td><?= e(dt($r['fecha_inicio'] ?: $r['fecha_registro'])) ?></td><td><?= e($r['fecha_fin']?dt($r['fecha_fin']):'NA') ?></td><td><?= status_pill($r['estado_novedad']) ?></td><td class="no-print"><a href="<?= e(base_url('index.php?page=novelty_form&id='.$r['id'])) ?>">Editar</a> <?= delete_button('novelty',$r['id'],'index.php?page=novelties') ?></td></tr><?php endforeach; ?></tbody></table></div></div>
<div id="noveltyCalendarModal" class="modal-overlay calendar-modal-overlay" hidden><div class="calendar-modal google-calendar-modal"><div class="calendar-modal-actions"><button class="btn secondary" type="button" onclick="calendarToday()">Hoy</button><button class="btn secondary" type="button" onclick="calendarMove(-1)">‹ Anterior</button><button class="btn secondary" type="button" onclick="calendarMove(1)">Siguiente ›</button><select id="calendarViewMode" onchange="renderNoveltyCalendar()"><option value="month">Mes</option><option value="fortnight">Quincena</option><option value="week">Semana</option><option value="day">Día</option></select><button class="btn secondary" type="button" onclick="printCalendarModal()">Imprimir</button><button class="btn danger" type="button" onclick="closeNoveltyCalendar()">Cerrar</button></div><div id="calendar-print-area" class="calendar-report-card"><div class="novelty-report-header calendar-report-header"><div class="nrh-logo nrh-logo-left"><img src="<?= e(mandatos_logo_url()) ?>"></div><div class="nrh-title"><div>CALENDARIO DE NOVEDADES</div><span><?= e($reportCompany['nombre'] ?? 'Todas las empresas') ?></span><small>Generado: <?= e(date('d/m/Y H:i')) ?></small></div><div class="nrh-meta"><strong>Vista:</strong> <span id="calendarViewLabel">Mes</span><br><strong>Periodo:</strong> <span id="calendarPeriodLabel"></span><br><strong>Eventos:</strong> <?= e(count($events)) ?></div><div class="nrh-logo nrh-logo-right"><?php if($clientLogo): ?><img src="<?= e($clientLogo) ?>"><?php else: ?><strong><?= e($reportCompany['nombre'] ?? 'Todas') ?></strong><?php endif; ?></div></div><div id="calendarApp" class="calendar-app"></div></div></div></div>
<script>
const noveltyCalendarEvents=<?= json_encode($events,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) ?>;
let calendarCursor=new Date(); calendarCursor.setHours(12,0,0,0);
function openNoveltyCalendar(){document.getElementById('noveltyCalendarModal').hidden=false;document.body.classList.add('modal-open');calendarToday();}
function closeNoveltyCalendar(){document.getElementById('noveltyCalendarModal').hidden=true;document.body.classList.remove('modal-open');}
function calendarToday(){calendarCursor=new Date();calendarCursor.setHours(12,0,0,0);renderNoveltyCalendar();}
function calendarMove(dir){const v=document.getElementById('calendarViewMode').value;if(v==='month')calendarCursor.setMonth(calendarCursor.getMonth()+dir);else if(v==='fortnight')calendarCursor.setDate(calendarCursor.getDate()+15*dir);else if(v==='week')calendarCursor.setDate(calendarCursor.getDate()+7*dir);else calendarCursor.setDate(calendarCursor.getDate()+dir);renderNoveltyCalendar();}
function isoDate(d){return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');}
function startOfWeek(d){const x=new Date(d),day=(x.getDay()+6)%7;x.setDate(x.getDate()-day);x.setHours(12,0,0,0);return x;}
function esc(s){return String(s??'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
function eventsFor(date){const k=isoDate(date);return noveltyCalendarEvents.filter(e=>e.date===k);}
function eventHtml(e){const cls=e.kind.indexOf('Terminación')===0?'cal-event-end':'cal-event-start';return `<div class="gcal-event ${cls}" title="${esc(e.name)} · ${esc(e.title)}"><b>${esc(e.kind)}</b><span>${esc(e.name)}</span><small>${esc(e.title)}</small></div>`;}
function dayCell(d,other=false){const ev=eventsFor(d);return `<div class="gcal-day ${other?'other-month':''} ${isoDate(d)===isoDate(new Date())?'today':''}"><div class="gcal-day-num">${d.getDate()}</div><div class="gcal-events">${ev.map(eventHtml).join('')}</div></div>`;}
function renderNoveltyCalendar(){const app=document.getElementById('calendarApp'),view=document.getElementById('calendarViewMode').value;document.getElementById('calendarViewLabel').textContent={month:'Mes',fortnight:'Quincena',week:'Semana',day:'Día'}[view];let html='',start,end;
 if(view==='month'){start=new Date(calendarCursor.getFullYear(),calendarCursor.getMonth(),1,12);let grid=startOfWeek(start);end=new Date(grid);end.setDate(end.getDate()+41);document.getElementById('calendarPeriodLabel').textContent=start.toLocaleDateString('es-CO',{month:'long',year:'numeric'});html='<div class="gcal-weekdays">'+['Lun','Mar','Mié','Jue','Vie','Sáb','Dom'].map(x=>`<div>${x}</div>`).join('')+'</div><div class="gcal-month-grid">';for(let i=0;i<42;i++){let d=new Date(grid);d.setDate(grid.getDate()+i);html+=dayCell(d,d.getMonth()!==calendarCursor.getMonth());}html+='</div>';
 }else{let days=view==='fortnight'?15:(view==='week'?7:1);start=view==='week'?startOfWeek(calendarCursor):new Date(calendarCursor);if(view==='fortnight'){start.setDate(calendarCursor.getDate()<=15?1:16);}end=new Date(start);end.setDate(start.getDate()+days-1);document.getElementById('calendarPeriodLabel').textContent=start.toLocaleDateString('es-CO')+' - '+end.toLocaleDateString('es-CO');html='<div class="gcal-agenda">';for(let i=0;i<days;i++){let d=new Date(start);d.setDate(start.getDate()+i);html+=`<section class="gcal-agenda-day ${isoDate(d)===isoDate(new Date())?'today':''}"><header><strong>${d.toLocaleDateString('es-CO',{weekday:'short',day:'numeric',month:'short'})}</strong></header><div>${eventsFor(d).length?eventsFor(d).map(eventHtml).join(''):'<span class="muted">Sin novedades</span>'}</div></section>`;}html+='</div>';}
 app.innerHTML=html;}
function printCalendarModal(){
 const area=document.getElementById('calendar-print-area');
 if(!area){alert('No se encontró el calendario para imprimir.');return;}
 const view=document.getElementById('calendarViewLabel')?.textContent||'Calendario';
 const period=document.getElementById('calendarPeriodLabel')?.textContent||'';
 const title='Calendario de novedades - '+view+(period?' - '+period:'');
 const w=window.open('','_blank','width=1200,height=760');
 if(!w){alert('El navegador bloqueó la ventana de impresión. Permita las ventanas emergentes para este sitio.');return;}
 const css=`
 @page{size:Letter landscape;margin:8mm}
 *{box-sizing:border-box}
 html,body{margin:0;padding:0;background:#fff;color:#0b2630;font-family:Arial,sans-serif}
 body{width:100%;font-size:8pt}
 .calendar-report-card{width:100%;max-width:none;margin:0;padding:0;background:#fff}
 .novelty-report-header{display:grid;grid-template-columns:18% 1fr 23% 16%;align-items:center;gap:8px;border:1px solid #c8dce3;border-bottom:4px solid #01495E;padding:8px 10px;margin:0 0 8px;border-radius:8px;background:#fff}
 .nrh-logo{display:flex;align-items:center;justify-content:center;min-height:48px}
 .nrh-logo img{display:block;max-width:100%;max-height:52px;width:auto;height:auto;object-fit:contain}
 .nrh-title{text-align:center;line-height:1.15}.nrh-title>div{font-size:15pt;font-weight:800;letter-spacing:.04em}.nrh-title span{display:block;font-size:8pt;font-weight:700;color:#01495E;margin-top:2px}.nrh-title small{display:block;font-size:7pt;margin-top:2px}
 .nrh-meta{font-size:7pt;line-height:1.35;border-left:2px solid #01495E;padding-left:8px}
 .calendar-app{width:100%}
 .gcal-weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#01495E;color:#fff;border:1px solid #01495E}
 .gcal-weekdays>div{text-align:center;font-weight:700;padding:4px 2px;font-size:7pt}
 .gcal-month-grid{display:grid;grid-template-columns:repeat(7,1fr);border-left:1px solid #9db7bf;border-top:1px solid #9db7bf}
 .gcal-day{min-height:24mm;border-right:1px solid #9db7bf;border-bottom:1px solid #9db7bf;padding:3px;background:#fff;overflow:hidden}
 .gcal-day.other-month{background:#f4f7f8;color:#7a8b90}.gcal-day.today{box-shadow:inset 0 0 0 2px #6DC2D5;background:#f0fbfd}
 .gcal-day-num{font-weight:800;font-size:7pt;margin-bottom:2px}
 .gcal-events{display:flex;flex-direction:column;gap:2px}
 .gcal-event{border-radius:3px;padding:2px 3px;font-size:5.8pt;line-height:1.15;border-left:3px solid #0b5c6b;background:#eaf5f7;overflow:hidden}
 .gcal-event.cal-event-end{border-left-color:#bf2d2d;background:#fff0f0}.gcal-event b,.gcal-event span,.gcal-event small{display:block;white-space:normal;overflow-wrap:break-word}.gcal-event small{font-size:5.3pt;color:#455}
 .gcal-agenda{display:block;border:1px solid #9db7bf;border-radius:6px;overflow:hidden}.gcal-agenda-day{display:grid;grid-template-columns:22% 1fr;border-bottom:1px solid #c5d5da;min-height:18mm}.gcal-agenda-day:last-child{border-bottom:0}.gcal-agenda-day header{background:#edf7f9;padding:6px;font-size:8pt}.gcal-agenda-day>div{padding:5px}.gcal-agenda-day .gcal-event{font-size:7pt;margin-bottom:3px}
 .muted{color:#6b7b80;font-style:italic}
 @media print{body{print-color-adjust:exact;-webkit-print-color-adjust:exact}.calendar-report-card{page-break-inside:auto}.gcal-day,.gcal-agenda-day{break-inside:avoid}}
 `;
 w.document.open();
 w.document.write('<!doctype html><html><head><meta charset="utf-8"><base href="'+document.baseURI+'"><title>'+esc(title)+'</title><style>'+css+'</style></head><body>'+area.outerHTML+'</body></html>');
 w.document.close();
 const doPrint=()=>{w.focus();w.print();};
 if(w.document.readyState==='complete') setTimeout(doPrint,700); else w.onload=()=>setTimeout(doPrint,700);
}
</script>
<?php footer_html(); }

function report_excel_text($v){ return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
function report_absolute_url($url){
    $url=trim((string)$url); if($url==='') return '';
    if(preg_match('/^https?:\/\//i',$url)) return $url;
    $scheme=(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off')?'https':'http';
    $host=$_SERVER['HTTP_HOST'] ?? '';
    if(isset($url[0]) && $url[0]==='/') return $host ? $scheme.'://'.$host.$url : $url;
    $base=trim((string)cfg('base_url',''));
    if($base!=='') return rtrim($base,'/').'/'.ltrim($url,'/');
    $dir=rtrim(str_replace('\\','/',dirname($_SERVER['SCRIPT_NAME'] ?? '/')),'/');
    return $host ? $scheme.'://'.$host.($dir?'/'.ltrim($dir,'/'):'').'/'.ltrim($url,'/') : $url;
}
function report_excel_img_tag($url,$alt='Logo',$w=120,$h=62){
    $url=report_absolute_url($url); if($url==='') return '&nbsp;';
    return '<img src="'.report_excel_text($url).'" alt="'.report_excel_text($alt).'" width="'.$w.'" height="'.$h.'" style="display:block;width:'.$w.'px;height:'.$h.'px;max-width:'.$w.'px;max-height:'.$h.'px;border:0;margin:0 auto;">';
}
function report_company_for_header($rows,$company_id=0){
    if($company_id) return get_company($company_id);
    if(!$rows) return null;
    $firstCompany=$rows[0]['company_id'] ?? null;
    foreach($rows as $r){ if((string)($r['company_id'] ?? '') !== (string)$firstCompany) return null; }
    return $firstCompany ? get_company($firstCompany) : null;
}
function report_doc_meta($company=null){ return ['codigo'=>'GE-VG-65','version'=>'001','fecha'=>'30/06/2024']; }
function report_novedades_header_excel_html($rows,$company_id=0){
    $company=report_company_for_header($rows,$company_id); $clientLogo=$company && !empty($company['logo_ruta']) ? public_file_url($company['logo_ruta']) : '';
    $mandatos=mandatos_logo_url(); $meta=report_doc_meta($company); $empresaNombre=$company['nombre'] ?? 'Todas las empresas'; $generado=date('d/m/Y H:i');
    $logoCliente=$clientLogo ? report_excel_img_tag($clientLogo,'Logo empresa',110,56) : '<span style="font-weight:bold;color:#01495E;">'.report_excel_text($empresaNombre).'</span>';
    return '<table class="excel-header-table" cellspacing="0" cellpadding="0" border="0" style="width:100%;border-collapse:collapse;table-layout:fixed;">'
        .'<tr style="height:56px;"><td colspan="2" rowspan="3" style="text-align:center;vertical-align:middle;">'.report_excel_img_tag($mandatos,'Mandatos',128,70).'</td><td colspan="4" rowspan="2" style="text-align:center;vertical-align:middle;font-size:18pt;font-weight:bold;letter-spacing:.04em;">INFORME DE NOVEDADES</td><td colspan="2" style="font-size:10pt;text-align:center;vertical-align:middle;border-left:3px solid #01495E;border-right:3px solid #01495E;"><b>Código:</b> '.report_excel_text($meta['codigo']).'</td><td rowspan="3" style="text-align:center;vertical-align:middle;">'.$logoCliente.'</td></tr>'
        .'<tr style="height:24px;"><td colspan="2" style="font-size:10pt;text-align:center;vertical-align:middle;border-left:3px solid #01495E;border-right:3px solid #01495E;"><b>Versión:</b> '.report_excel_text($meta['version']).'</td></tr>'
        .'<tr style="height:24px;"><td colspan="4" style="text-align:center;vertical-align:middle;font-size:8pt;color:#01495E;font-weight:bold;">'.report_excel_text($empresaNombre).' &nbsp; | &nbsp; Generado: '.report_excel_text($generado).'</td><td colspan="2" style="font-size:10pt;text-align:center;vertical-align:middle;border-left:3px solid #01495E;border-right:3px solid #01495E;"><b>Fecha:</b> '.report_excel_text($meta['fecha']).'</td></tr>'
        .'<tr style="height:10px;"><td colspan="9" style="border-bottom:3px solid #01495E;">&nbsp;</td></tr><tr style="height:8px;"><td colspan="9">&nbsp;</td></tr></table>';
}
function report_novedades_header_html($rows,$company_id=0,$mode='screen'){
    $company=report_company_for_header($rows,$company_id); $clientLogo=$company && !empty($company['logo_ruta']) ? public_file_url($company['logo_ruta']) : '';
    $mandatos=mandatos_logo_url(); $meta=report_doc_meta($company); $empresaNombre=$company['nombre'] ?? 'Todas las empresas';
    ob_start(); ?>
    <div class="novelty-report-header <?= $mode==='excel'?'excel-header':'' ?>">
        <div class="nrh-logo nrh-logo-left"><img src="<?= e($mode==='excel'?report_absolute_url($mandatos):$mandatos) ?>" alt="Mandatos"></div>
        <div class="nrh-title"><div>INFORME DE NOVEDADES</div><span><?= e($empresaNombre) ?></span><small>Generado: <?= e(date('d/m/Y H:i')) ?></small></div>
        <div class="nrh-meta"><strong>Código:</strong> <?= e($meta['codigo']) ?><br><strong>Versión:</strong> <?= e($meta['version']) ?><br><strong>Fecha:</strong> <?= e($meta['fecha']) ?></div>
        <div class="nrh-logo nrh-logo-right"><?php if($clientLogo): ?><img src="<?= e($mode==='excel'?report_absolute_url($clientLogo):$clientLogo) ?>" alt="Logo empresa"><?php else: ?><strong><?= e($empresaNombre) ?></strong><?php endif; ?></div>
    </div>
    <?php return ob_get_clean();
}
function report_colaboradores_header_html($rows,$company_id=0){
    $company=report_company_for_header($rows,$company_id); $clientLogo=$company && !empty($company['logo_ruta']) ? public_file_url($company['logo_ruta']) : '';
    $mandatos=mandatos_logo_url(); $meta=report_doc_meta($company); $empresaNombre=$company['nombre'] ?? 'Todas las empresas';
    ob_start(); ?>
    <div class="novelty-report-header collaborator-report-header">
        <div class="nrh-logo nrh-logo-left"><img src="<?= e($mandatos) ?>" alt="Mandatos"></div>
        <div class="nrh-title"><div>INFORME DE COLABORADORES</div><span><?= e($empresaNombre) ?></span><small>Generado: <?= e(date('d/m/Y H:i')) ?></small></div>
        <div class="nrh-meta"><strong>Código:</strong> <?= e($meta['codigo']) ?><br><strong>Versión:</strong> <?= e($meta['version']) ?><br><strong>Fecha:</strong> <?= e($meta['fecha']) ?></div>
        <div class="nrh-logo nrh-logo-right"><?php if($clientLogo): ?><img src="<?= e($clientLogo) ?>" alt="Logo empresa"><?php else: ?><strong><?= e($empresaNombre) ?></strong><?php endif; ?></div>
    </div>
    <?php return ob_get_clean();
}
function collaborator_report_context_html($rows){
    $parts=['Registros: '.count($rows)];
    if(!empty($_GET['estado'])) $parts[]='Estado: '.$_GET['estado'];
    if(!empty($_GET['tipo_vinculacion'])) $parts[]='Contrato: '.$_GET['tipo_vinculacion'];
    if(!empty($_GET['q'])) $parts[]='Búsqueda: '.$_GET['q'];
    if(!empty($_GET['nov_categoria'])) $parts[]='Novedad: '.$_GET['nov_categoria'];
    if(!empty($_GET['nov_tipo'])) $parts[]='Tipo novedad: '.$_GET['nov_tipo'];
    if(!empty($_GET['nov_fi']) || !empty($_GET['nov_ff'])) $parts[]='Periodo novedad: '.(dt($_GET['nov_fi'] ?? '') ?: 'inicio').' a '.(dt($_GET['nov_ff'] ?? '') ?: 'fin');
    $html='<div class="report-context collaborator-context">'; foreach($parts as $p){$html.='<span>'.e($p).'</span>';} return $html.'</div>';
}
function report_context_html($rtype,$rows){
    $parts=[]; if($rtype==='novelties'){ if(!empty($_GET['fi']) || !empty($_GET['ff'])) $parts[]='Periodo: '.(dt($_GET['fi'] ?? '') ?: 'inicio').' a '.(dt($_GET['ff'] ?? '') ?: 'fin'); if(!empty($_GET['categoria'])) $parts[]='Categoría: '.$_GET['categoria']; if(!empty($_GET['quincena'])) $parts[]='Quincena: '.$_GET['quincena']; $parts[]='Registros: '.count($rows); }
    if(!$parts) return ''; $html='<div class="report-context novelty-context">'; foreach($parts as $p){ $html.='<span>'.e($p).'</span>'; } return $html.'</div>';
}
function build_report_query($export=false){
    $u=current_user(); $rtype=$_GET['report_type'] ?? 'novelties';
    if($rtype==='collaborators'){
        $company=(int)($_GET['company_id']??0); $estado=$_GET['estado']??''; $tipo=$_GET['tipo_vinculacion']??''; $q=trim($_GET['q']??'');
        $novCat=$_GET['nov_categoria']??''; $novTipo=trim($_GET['nov_tipo']??''); $novFi=$_GET['nov_fi']??''; $novFf=$_GET['nov_ff']??'';
        $sql='SELECT DISTINCT c.*, co.nombre company_name FROM collaborators c JOIN companies co ON co.id=c.company_id'; $params=[]; $where=' WHERE 1=1';
        if($novCat || $novTipo || $novFi || $novFf){ $sql.=' JOIN novelties nf ON nf.collaborator_id=c.id'; $where.=' AND nf.company_id=c.company_id'; }
        if(user_restricts_company()){ $where.=' AND c.company_id=?'; $params[]=$u['company_id']; } elseif($company){ $where.=' AND c.company_id=?'; $params[]=$company; }
        if($estado){ $where.=' AND c.estado=?'; $params[]=$estado; } if($tipo){ $where.=' AND c.tipo_vinculacion=?'; $params[]=$tipo; }
        if($q){ $where.=' AND (c.nombre_completo LIKE ? OR c.numero_documento LIKE ? OR c.cargo LIKE ? OR c.area LIKE ?)'; array_push($params,"%$q%","%$q%","%$q%","%$q%"); }
        if($novCat){ $where.=category_filter_sql('nf',$novCat,$params); } if($novTipo){ $where.=' AND nf.tipo_novedad LIKE ?'; $params[]="%$novTipo%"; } if($novFi){ $where.=' AND COALESCE(nf.fecha_inicio,nf.fecha_registro)>=?'; $params[]=$novFi; } if($novFf){ $where.=' AND COALESCE(nf.fecha_inicio,nf.fecha_registro)<=?'; $params[]=$novFf; }
        $sql.=$where.' ORDER BY FIELD(c.estado,"Prospecto","En selección","Activo","Contratado","Suspendido","Retirado","Inactivo","Candidato no seleccionado"), c.nombre_completo '.($export?'':' LIMIT 2000'); return [$sql,$params,'collaborators'];
    }
    $company=(int)($_GET['company_id']??0); $coll=(int)($_GET['collaborator_id']??0); $categoria=$_GET['categoria']??''; $tipo=trim($_GET['tipo']??''); $fi=$_GET['fi']??''; $ff=$_GET['ff']??''; $quincena=$_GET['quincena']??''; $sql='SELECT '.novelty_select_columns().' FROM novelties n LEFT JOIN collaborators c ON c.id=n.collaborator_id JOIN companies co ON co.id=n.company_id WHERE 1=1'; $params=[]; if(user_restricts_company()){ $sql.=' AND n.company_id=?'; $params[]=$u['company_id']; } elseif($company){ $sql.=' AND n.company_id=?'; $params[]=$company; } if($coll){ $sql.=' AND n.collaborator_id=?'; $params[]=$coll; } $sql.=category_filter_sql('n',$categoria,$params); if($tipo){ $sql.=' AND n.tipo_novedad LIKE ?'; $params[]="%$tipo%"; } if($fi){ $sql.=' AND COALESCE(n.fecha_inicio,n.fecha_registro)>=?'; $params[]=$fi; } if($ff){ $sql.=' AND COALESCE(n.fecha_inicio,n.fecha_registro)<=?'; $params[]=$ff; } if($quincena){ $sql.=' AND n.quincena=?'; $params[]=$quincena; } $sql.=' ORDER BY COALESCE(n.fecha_inicio,n.fecha_registro) DESC, n.id DESC '.($export?'':' LIMIT 2000'); return [$sql,$params,'novelties'];
}
function export_report_csv(){ require_login(); [$sql,$params,$rtype]=build_report_query(true); $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll(); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="informe_'.$rtype.'_'.date('Ymd_His').'.csv"'); echo "\xEF\xBB\xBF"; $out=fopen('php://output','w'); if($rtype==='novelties'){ fputcsv($out,['Fecha','Quincena','Empresa','Colaborador','Documento','Categoría','Tipo','Inicio/Gestión','Terminación'],';'); foreach($rows as $r) fputcsv($out,[dt($r['fecha_registro']),$r['quincena'],$r['company_name'],$r['nombre_completo'],$r['numero_documento'],$r['categoria'],$r['tipo_novedad'],dt($r['fecha_inicio']?:$r['fecha_registro']),$r['fecha_fin']?dt($r['fecha_fin']):'NA'],';'); } else { fputcsv($out,report_collaborator_columns(),';'); foreach($rows as $r) fputcsv($out,report_collaborator_row($r),';'); } exit; }
function export_report_excel(){ require_login(); [$sql,$params,$rtype]=build_report_query(true); $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll(); $filename=($rtype==='novelties'?'informe_novedades':'informe_colaboradores').'_'.date('Ymd_His').'.xls'; header('Content-Type: application/vnd.ms-excel; charset=utf-8'); header('Content-Disposition: attachment; filename="'.$filename.'"'); header('Cache-Control: max-age=0'); echo "\xEF\xBB\xBF"; ?><!doctype html><html><head><meta charset="utf-8"><style>@page{mso-page-orientation:landscape;margin:.25in .18in .25in .18in;mso-fit-to-page:yes;}body{font-family:Arial,sans-serif;font-size:8pt;color:#111}.report-table{border-collapse:collapse;width:100%;table-layout:fixed;font-size:8pt}.report-table th{background:#2f69bf;color:#fff;font-weight:700;border:1px solid #9eb7e4;padding:3px;text-align:left}.report-table td{border:1px solid #b8c9e8;padding:2px 3px;vertical-align:top;mso-number-format:'\@'}.report-table tr:nth-child(even) td{background:#dbe7f7}.collab-report-table{font-size:7pt}.collab-report-table th,.collab-report-table td{padding:2px;word-wrap:break-word}.c-fecha{width:7%}.c-quincena{width:8%}.c-empresa{width:14%}.c-colaborador{width:22%}.c-documento{width:10%}.c-categoria{width:13%}.c-tipo{width:16%}.c-inicio{width:5%}.c-fin{width:5%}</style></head><body><?php if($rtype==='novelties'){ echo report_novedades_header_excel_html($rows,(int)($_GET['company_id']??0)); echo '<table class="report-table"><colgroup><col class="c-fecha"><col class="c-quincena"><col class="c-empresa"><col class="c-colaborador"><col class="c-documento"><col class="c-categoria"><col class="c-tipo"><col class="c-inicio"><col class="c-fin"></colgroup><thead><tr><th>FECHA</th><th>QUINCENA</th><th>EMPRESA</th><th>COLABORADOR</th><th>DOCUMENTO</th><th>CATEGORÍA</th><th>TIPO</th><th>INICIO/GESTIÓN</th><th>TERMINACIÓN</th></tr></thead><tbody>'; foreach($rows as $r){ echo '<tr><td>'.report_excel_text(dt($r['fecha_registro'])).'</td><td>'.report_excel_text($r['quincena']).'</td><td>'.report_excel_text($r['company_name']).'</td><td>'.report_excel_text($r['nombre_completo']).'</td><td>'.report_excel_text($r['numero_documento']).'</td><td>'.report_excel_text($r['categoria']).'</td><td>'.report_excel_text($r['tipo_novedad']).'</td><td>'.report_excel_text(dt($r['fecha_inicio']?:$r['fecha_registro'])).'</td><td>'.report_excel_text($r['fecha_fin']?dt($r['fecha_fin']):'NA').'</td></tr>'; } echo '</tbody></table>'; } else { echo '<h2>Informe de colaboradores</h2><table class="report-table collab-report-table"><thead><tr>'; foreach(report_collaborator_columns() as $h){ echo '<th>'.report_excel_text($h).'</th>'; } echo '</tr></thead><tbody>'; foreach($rows as $r){ echo '<tr>'; foreach(report_collaborator_row($r) as $v){ echo '<td>'.report_excel_text($v).'</td>'; } echo '</tr>'; } echo '</tbody></table>'; } ?></body></html><?php exit; }
function reports(){ require_login(); $export=$_GET['export']??''; if($export==='csv') return export_report_csv(); if($export==='excel') return export_report_excel(); [$sql,$params,$rtype]=build_report_query(false); $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll(); $company=(int)($_GET['company_id']??0); header_html('Informes'); ?>
<div class="sticky-controls no-print"><div class="card"><form method="get" class="filters"><input type="hidden" name="page" value="reports"><div><label>Tipo de informe</label><select name="report_type" onchange="this.form.submit()"><option value="novelties" <?= $rtype==='novelties'?'selected':'' ?>>Novedades</option><option value="collaborators" <?= $rtype==='collaborators'?'selected':'' ?>>Colaboradores</option></select></div><div><label>Empresa</label><select id="companySelect" name="company_id" onchange="this.form.submit()"><option value="">Todas</option><?= company_options($company) ?></select></div><?php if($rtype==='novelties'): ?><div><label>Colaborador</label><select id="collaboratorSelect" class="combo-collaborator" name="collaborator_id"><?= collaborator_options($company,$_GET['collaborator_id']??'') ?></select></div><div><label>Categoría</label><select name="categoria"><?= option_list(categories(),$_GET['categoria']??'','Todas') ?></select></div><div><label>Tipo contiene</label><input name="tipo" value="<?= e($_GET['tipo']??'') ?>"></div><div><label>Fecha inicial</label><input type="date" name="fi" value="<?= e($_GET['fi']??'') ?>"></div><div><label>Fecha final</label><input type="date" name="ff" value="<?= e($_GET['ff']??'') ?>"></div><div><label>Quincena</label><input name="quincena" value="<?= e($_GET['quincena']??'') ?>"></div><?php else: ?><div><label>Estado</label><select name="estado"><?= option_list(estados_colaborador(),$_GET['estado']??'','Todos') ?></select></div><div><label>Tipo contrato</label><select name="tipo_vinculacion"><?= option_list(tipos_contrato(),$_GET['tipo_vinculacion']??'','Todos') ?></select></div><div><label>Búsqueda</label><input name="q" value="<?= e($_GET['q']??'') ?>"></div><div class="filter-separator"><strong>Filtrar colaboradores por novedad</strong></div><div><label>Categoría novedad</label><select name="nov_categoria"><?= option_list(categories(),$_GET['nov_categoria']??'','Todas') ?></select></div><div><label>Tipo novedad contiene</label><input name="nov_tipo" value="<?= e($_GET['nov_tipo']??'') ?>" placeholder="Ej. Ingreso"></div><div><label>Fecha inicial novedad</label><input type="date" name="nov_fi" value="<?= e($_GET['nov_fi']??'') ?>"></div><div><label>Fecha final novedad</label><input type="date" name="nov_ff" value="<?= e($_GET['nov_ff']??'') ?>"></div><?php endif; ?><div><label>&nbsp;</label><button class="btn">Generar</button></div></form></div><div class="grid cols-3"><div class="kpi"><div class="num"><?= e(count($rows)) ?></div><div class="label">Registros encontrados</div></div><?php if($rtype==='novelties'): ?><div class="kpi"><div class="num"><?= e(number_format(array_sum(array_map(fn($r)=>(float)$r['dias'],$rows)),2,',','.')) ?></div><div class="label">Días reportados</div></div><div class="kpi"><div class="num"><?= e(money(array_sum(array_map(fn($r)=>(float)$r['valor'],$rows)))) ?></div><div class="label">Valor total</div></div><?php else: ?><div class="kpi"><div class="num"><?= e(count(array_filter($rows,fn($r)=>in_array($r['estado'],['Activo','Contratado'],true)))) ?></div><div class="label">Activos / contratados</div></div><div class="kpi"><div class="num"><?= e(money(array_sum(array_map(fn($r)=>(float)$r['salario_base'],$rows)))) ?></div><div class="label">Suma salarios</div></div><?php endif; ?></div><div class="card compact-card"><div class="actions"><a class="btn secondary" href="<?= e(base_url('index.php?'.http_build_query(array_merge($_GET,['export'=>'csv'])))) ?>">Exportar CSV</a><a class="btn secondary" href="<?= e(base_url('index.php?'.http_build_query(array_merge($_GET,['export'=>'excel'])))) ?>">Exportar Excel</a><?php $shareParams=$_GET; unset($shareParams['export']); $shareScreen=app_absolute_url(base_url('index.php?'.http_build_query($shareParams))); $shareExcel=app_absolute_url(base_url('index.php?'.http_build_query(array_merge($shareParams,['export'=>'excel'])))); $shareCsv=app_absolute_url(base_url('index.php?'.http_build_query(array_merge($shareParams,['export'=>'csv'])))); $shareTitle=$rtype==='novelties'?'informe de novedades':'informe de colaboradores'; ?><?= whatsapp_button_html('WhatsApp informe','','Buenos días. Remito '.$shareTitle.' generado desde Mandatos GE: '.$shareScreen) ?><?= whatsapp_button_html('WhatsApp Excel','','Buenos días. Remito enlace de exportación Excel del '.$shareTitle.': '.$shareExcel) ?><?= whatsapp_button_html('WhatsApp CSV','','Buenos días. Remito enlace de exportación CSV del '.$shareTitle.': '.$shareCsv) ?><button class="btn secondary" onclick="copyTable('tabla-informe')">Copiar tabla</button><button class="btn secondary" type="button" onclick="printCurrentReport('<?= e($rtype) ?>')">Imprimir PDF</button><?= whatsapp_button_html('WhatsApp PDF','','Buenos días. Remito enlace del informe para visualizar o imprimir en PDF: '.$shareScreen) ?></div></div></div>
<?php $reportCompany=report_company_for_header($rows,$company); $clientLogo=$reportCompany && !empty($reportCompany['logo_ruta'])?public_file_url($reportCompany['logo_ruta']):''; ?>
<div id="report-print-area" class="card report-card <?= $rtype==='novelties'?'report-novedades-print':'report-colaboradores-print' ?>"><?php if($rtype==='novelties'): ?><?= report_novedades_header_html($rows,$company,'screen') ?><?= report_context_html($rtype,$rows) ?><?php else: ?><?= report_colaboradores_header_html($rows,$company) ?><?= collaborator_report_context_html($rows) ?><?php endif; ?><div class="table-wrap results-table-wrap"><table id="tabla-informe" class="sortable novelty-report-table <?= $rtype==='collaborators'?'collab-report-full-table':'' ?>"><?php if($rtype==='novelties'): ?><colgroup><col class="c-fecha"><col class="c-quincena"><col class="c-empresa"><col class="c-colaborador"><col class="c-documento"><col class="c-categoria"><col class="c-tipo"><col class="c-inicio"><col class="c-fin"></colgroup><thead><tr><th>FECHA</th><th>QUINCENA</th><th>EMPRESA</th><th>COLABORADOR</th><th>DOCUMENTO</th><th>CATEGORÍA</th><th>TIPO</th><th>INICIO/GESTIÓN</th><th>TERMINACIÓN</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e(dt($r['fecha_registro'])) ?></td><td><?= e($r['quincena']) ?></td><td><?= e($r['company_name']) ?></td><td><?= e($r['nombre_completo']) ?></td><td><?= e($r['numero_documento']) ?></td><td><?= e($r['categoria']) ?></td><td><?= e($r['tipo_novedad']) ?></td><td><?= e(dt($r['fecha_inicio']?:$r['fecha_registro'])) ?></td><td><?= e($r['fecha_fin']?dt($r['fecha_fin']):'NA') ?></td></tr><?php endforeach; ?></tbody><?php else: ?><colgroup class="collab-colgroup"><col style="width:11%"><col style="width:3%"><col style="width:6%"><col style="width:5%"><col style="width:9%"><col style="width:4%"><col style="width:6%"><col style="width:7%"><col style="width:5%"><col style="width:5%"><col style="width:4%"><col style="width:4%"><col style="width:6%"><col style="width:6%"><col style="width:5%"><col style="width:4%"><col style="width:5%"><col style="width:5%"></colgroup><thead><tr><?php foreach(report_collaborator_columns() as $h): ?><th><?= e($h) ?></th><?php endforeach; ?></tr></thead><tbody><?php foreach($rows as $r): ?><tr><?php foreach(report_collaborator_row($r) as $v): ?><td><?= e($v) ?></td><?php endforeach; ?></tr><?php endforeach; ?></tbody><?php endif; ?></table></div></div>
<style id="report-layout-v23">
#report-print-area.report-colaboradores-print{background:#fff;padding:14px;overflow:hidden}
#report-print-area .collaborator-report-header{display:grid;grid-template-columns:16% 1fr 18% 15%;gap:10px;align-items:center;border:1px solid #c9dce2;border-bottom:4px solid #01495E;border-radius:12px;padding:10px 12px;background:#fff;margin-bottom:10px}
#report-print-area .collaborator-report-header .nrh-logo{display:flex;justify-content:center;align-items:center;min-height:62px}
#report-print-area .collaborator-report-header .nrh-logo img{max-width:100%;max-height:62px;object-fit:contain}
#report-print-area .collaborator-report-header .nrh-title{text-align:center;line-height:1.15}
#report-print-area .collaborator-report-header .nrh-title>div{font-size:23px;font-weight:900;letter-spacing:.03em;color:#082e3a}
#report-print-area .collaborator-report-header .nrh-title span{display:block;font-weight:800;color:#01495E;margin-top:3px}
#report-print-area .collaborator-report-header .nrh-title small{display:block;margin-top:3px;font-size:11px}
#report-print-area .collaborator-report-header .nrh-meta{font-size:11px;line-height:1.45;border-left:3px solid #01495E;padding-left:10px}
#report-print-area .collaborator-context{display:flex;flex-wrap:wrap;gap:7px;margin:8px 0 10px}
#report-print-area .collaborator-context span{display:inline-flex;align-items:center;border:1px solid #b7d8e1;background:#f1fafc;border-radius:999px;padding:5px 10px;font-size:11px;font-weight:700;color:#01495E}
#report-print-area .results-table-wrap{overflow:auto;max-height:none}
#report-print-area .collab-report-full-table{width:100%;min-width:1550px;table-layout:fixed;border-collapse:collapse;font-size:11px}
#report-print-area .collab-report-full-table th{padding:7px 5px;background:#01495E;color:#fff;border:1px solid #407b89;white-space:normal;line-height:1.12;text-align:center;vertical-align:middle;word-break:normal;overflow-wrap:break-word}
#report-print-area .collab-report-full-table td{padding:6px 5px;border:1px solid #bfd0d5;vertical-align:top;line-height:1.2;white-space:normal;word-break:normal;overflow-wrap:break-word;background:#fff}
#report-print-area .collab-report-full-table tbody tr:nth-child(even) td{background:#f4f8f9}
#report-print-area .collab-report-full-table th::after{display:none!important;content:none!important}
</style>
<script>
function printCurrentReport(type){
 const area=document.getElementById('report-print-area');
 if(!area){alert('No se encontró el informe para imprimir.');return;}
 const isColl=type==='collaborators';
 const title=(isColl?'Informe de colaboradores':'Informe de novedades')+' - '+new Date().toLocaleDateString('es-CO');
 const w=window.open('','_blank','width=1250,height=850');
 if(!w){alert('El navegador bloqueó la ventana de impresión. Permita las ventanas emergentes para este sitio.');return;}
 const common=`*{box-sizing:border-box}html,body{margin:0;padding:0;background:#fff;color:#102a33;font-family:Arial,sans-serif}body{font-size:8pt}.report-card{border:0!important;box-shadow:none!important;margin:0!important;padding:0!important;background:#fff!important}.table-wrap{overflow:visible!important;max-height:none!important}.sortable th::after{display:none!important;content:none!important}.novelty-report-header{display:grid;grid-template-columns:16% 1fr 18% 15%;gap:8px;align-items:center;border:1px solid #c9dce2;border-bottom:4px solid #01495E;border-radius:7px;padding:7px 8px;background:#fff;margin-bottom:6px}.nrh-logo{display:flex;justify-content:center;align-items:center;min-height:42px}.nrh-logo img{display:block;max-width:100%;max-height:48px;width:auto;height:auto;object-fit:contain}.nrh-title{text-align:center;line-height:1.12}.nrh-title>div{font-size:14pt;font-weight:900;letter-spacing:.03em}.nrh-title span{display:block;font-size:7pt;font-weight:800;color:#01495E;margin-top:2px}.nrh-title small{display:block;font-size:6.5pt;margin-top:2px}.nrh-meta{font-size:6.5pt;line-height:1.35;border-left:2px solid #01495E;padding-left:6px}.report-context{display:flex;flex-wrap:wrap;gap:4px;margin:5px 0}.report-context span{border:1px solid #b7d8e1;background:#f3fafc;border-radius:10px;padding:3px 6px;font-size:6.5pt;font-weight:700;color:#01495E}.novelty-report-table{width:100%;border-collapse:collapse;table-layout:fixed}.novelty-report-table th{background:#01495E;color:#fff;border:1px solid #4f818d;padding:3px 2px;font-size:6.2pt;text-align:center;vertical-align:middle;line-height:1.1;white-space:normal;word-break:normal;overflow-wrap:break-word}.novelty-report-table td{border:1px solid #b9cdd2;padding:3px 2px;font-size:6.2pt;vertical-align:top;line-height:1.15;white-space:normal;word-break:normal;overflow-wrap:break-word}.novelty-report-table tbody tr:nth-child(even) td{background:#f4f8f9}.collab-report-full-table{min-width:0!important;width:100%!important;table-layout:fixed!important}.collab-report-full-table th,.collab-report-full-table td{font-size:5.8pt!important;padding:2.5px 2px!important;line-height:1.12!important}.collab-report-full-table th{font-size:5.5pt!important}.collab-report-full-table td:nth-child(2),.collab-report-full-table td:nth-child(9),.collab-report-full-table td:nth-child(10),.collab-report-full-table td:nth-child(11),.collab-report-full-table td:nth-child(12),.collab-report-full-table td:nth-child(13){text-align:center}.status{white-space:nowrap}.report-colaboradores-print{width:100%}.report-novedades-print{width:100%}@media print{body{print-color-adjust:exact;-webkit-print-color-adjust:exact}}`;
 const page=isColl?'@page{size:Letter landscape;margin:5mm}':'@page{size:Letter landscape;margin:6mm}';
 w.document.open();
 w.document.write('<!doctype html><html><head><meta charset="utf-8"><base href="'+document.baseURI+'"><title>'+title.replace(/</g,'&lt;')+'</title><style>'+page+common+'</style></head><body>'+area.outerHTML+'</body></html>');
 w.document.close();
 const doPrint=()=>{w.focus();w.print();};
 if(w.document.readyState==='complete') setTimeout(doPrint,700); else w.onload=()=>setTimeout(doPrint,700);
}
</script>
<?php footer_html(); }

function users(){ require_login(); if(!is_super()) exit('No autorizado'); $rows=db()->query('SELECT u.*, co.nombre company_name FROM users u LEFT JOIN companies co ON co.id=u.company_id ORDER BY u.nombre')->fetchAll(); header_html('Usuarios'); ?><div class="card"><a class="btn ok" href="<?= e(base_url('index.php?page=user_form')) ?>">Nuevo usuario</a></div><div class="card"><div class="table-wrap"><table class="sortable"><thead><tr><th>Nombre</th><th>Email</th><th>Rol</th><th>Empresa</th><th>Estado</th><th>Acciones</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e($r['nombre']) ?></td><td><?= e($r['email']) ?></td><td><?= e($r['rol']) ?></td><td><?= e($r['company_name'] ?: 'Todas') ?></td><td><?= status_pill($r['estado']) ?></td><td><a href="<?= e(base_url('index.php?page=user_form&id='.$r['id'])) ?>">Editar</a> <?= delete_button('user',$r['id'],'index.php?page=users') ?></td></tr><?php endforeach; ?></tbody></table></div></div><?php footer_html(); }
function user_form(){ require_login(); if(!is_super()) exit('No autorizado'); $id=(int)($_GET['id']??0); $r=['nombre'=>'','email'=>'','rol'=>'consulta','company_id'=>'','estado'=>'Activo']; if($id){ $st=db()->prepare('SELECT * FROM users WHERE id=?'); $st->execute([$id]); $r=$st->fetch() ?: $r; } if($_SERVER['REQUEST_METHOD']==='POST'){ check_csrf(); $hash=!empty($_POST['password'])?password_hash($_POST['password'],PASSWORD_DEFAULT):null; if($id){ if($hash){ db()->prepare('UPDATE users SET nombre=?,email=?,rol=?,company_id=?,estado=?,password_hash=? WHERE id=?')->execute([$_POST['nombre'],$_POST['email'],$_POST['rol'],$_POST['company_id']?:null,$_POST['estado'],$hash,$id]); } else { db()->prepare('UPDATE users SET nombre=?,email=?,rol=?,company_id=?,estado=? WHERE id=?')->execute([$_POST['nombre'],$_POST['email'],$_POST['rol'],$_POST['company_id']?:null,$_POST['estado'],$id]); } } else { db()->prepare('INSERT INTO users(nombre,email,rol,company_id,estado,password_hash) VALUES(?,?,?,?,?,?)')->execute([$_POST['nombre'],$_POST['email'],$_POST['rol'],$_POST['company_id']?:null,$_POST['estado'],$hash ?: password_hash('Temporal123*',PASSWORD_DEFAULT)]); } flash('ok','Usuario guardado.'); header('Location: '.base_url('index.php?page=users')); exit; } header_html($id?'Editar usuario':'Nuevo usuario'); ?><div class="card"><form method="post"><?= csrf_field() ?><div class="grid cols-2"><div class="field"><label>Nombre</label><input name="nombre" value="<?= e($r['nombre']) ?>"></div><div class="field"><label>Email</label><input name="email" value="<?= e($r['email']) ?>"></div><div class="field"><label>Rol</label><select name="rol"><?= option_list(['super_admin','admin_general','admin_empresa','consulta'],$r['rol']) ?></select></div><div class="field"><label>Empresa</label><select name="company_id"><option value="">Todas las empresas / acceso general</option><?= company_options($r['company_id']) ?></select></div><div class="field"><label>Estado</label><select name="estado"><option <?= $r['estado']==='Activo'?'selected':'' ?>>Activo</option><option <?= $r['estado']==='Inactivo'?'selected':'' ?>>Inactivo</option></select></div><div class="field"><label>Contraseña</label><input type="password" name="password" placeholder="Dejar en blanco para conservar"></div></div><button class="btn">Guardar</button></form></div><?php footer_html(); }
function audit_page(){ require_login(); if(!is_super()) exit('No autorizado'); $rows=db()->query('SELECT a.*, u.nombre user_name, co.nombre company_name FROM audit_logs a LEFT JOIN users u ON u.id=a.user_id LEFT JOIN companies co ON co.id=a.company_id ORDER BY a.created_at DESC LIMIT 500')->fetchAll(); header_html('Auditoría'); ?><div class="card"><div class="table-wrap"><table class="sortable"><thead><tr><th>Fecha</th><th>Usuario</th><th>Empresa</th><th>Acción</th><th>Tabla</th><th>Detalle</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e($r['created_at']) ?></td><td><?= e($r['user_name']) ?></td><td><?= e($r['company_name']) ?></td><td><?= e($r['accion']) ?></td><td><?= e($r['tabla_afectada']) ?></td><td><?= e($r['detalle']) ?></td></tr><?php endforeach; ?></tbody></table></div></div><?php footer_html(); }

function liquidation_types(){ return ['Vacaciones','Prima de servicios','Cesantías','Intereses cesantías','Liquidación final']; }
function round_money($v){ return round((float)$v,2); }
function days360_calc($start,$end){ if(!$start||!$end) return 0; $s=strtotime($start); $e=strtotime($end); if(!$s||!$e) return 0; $y1=(int)date('Y',$s); $m1=(int)date('m',$s); $d1=min((int)date('d',$s),30); $y2=(int)date('Y',$e); $m2=(int)date('m',$e); $d2=min((int)date('d',$e),30); return max(0,(($y2-$y1)*360+($m2-$m1)*30+($d2-$d1))+1); }
function liquidation_compute($input){
    $tipo=$input['tipo_liquidacion']??'Vacaciones';
    $salario=round_money($input['salario_base']??0); $variable=round_money($input['promedio_variable']??0); $aux=round_money($input['auxilio_transporte']??0);
    $diasNomina=(int)($input['dias_nomina'] ?? $input['dias_base'] ?? days360_calc($input['fecha_inicio']??null,$input['fecha_fin']??null));
    $diasCalendario=(int)($input['dias_calendario'] ?? 0); if(!$diasCalendario && !empty($input['fecha_inicio']) && !empty($input['fecha_fin'])){ $diasCalendario=max(0, floor((strtotime($input['fecha_fin'])-strtotime($input['fecha_inicio']))/86400)+1); }
    $diasBase=$diasNomina;
    $diasVac=round_money($input['dias_vacaciones']??0); $diasPend=round_money($input['dias_pendientes_vacaciones']??0); $salPend=round_money($input['valor_salarios_pendientes']??0); $dedManual=round_money($input['valor_deducciones']??0); $deduceVac=!empty($input['deduccion_vacaciones']);
    $basePrest=round_money($salario+$variable+$aux); $baseVac=round_money($salario+$variable); $items=[];
    $detalleDias=trim((string)($input['detalle_dias'] ?? '')); $detalleDesc=trim((string)($input['detalle_descuentos'] ?? ''));
    $obsExtra=trim('Días calendario: '.$diasCalendario.'. Días nómina: '.$diasNomina.'. '.($detalleDias?'Detalle días: '.$detalleDias.'. ':'').($detalleDesc?'Detalle descuentos: '.$detalleDesc.'. ':''));
    $add=function($concepto,$base,$dias,$tasa,$valor,$ded=0,$obs='') use (&$items,$obsExtra){ $items[]=['concepto'=>$concepto,'base'=>round_money($base),'dias'=>round_money($dias),'tasa'=>round_money($tasa),'valor'=>round_money($valor),'deduccion'=>round_money($ded),'neto'=>round_money($valor-$ded),'observacion'=>trim($obs.' '.$obsExtra)]; };
    $prima=0;$ces=0;$intCes=0;$vac=0;$ded=0;
    if($tipo==='Vacaciones'){
        if($diasVac<=0) $diasVac=round_money($diasBase*15/360); $vac=round_money(($baseVac/30)*$diasVac); $ded=$deduceVac?round_money($vac*0.08):$dedManual; $add('Vacaciones disfrutadas / compensadas',$baseVac,$diasVac,0,$vac,$ded,'Base vacaciones: salario ordinario + promedio variable. No incluye auxilio de transporte.');
    } elseif($tipo==='Prima de servicios'){
        $prima=round_money(($basePrest*$diasBase)/360); $add('Prima de servicios',$basePrest,$diasBase,0,$prima,0,'Base: salario + promedio variable + auxilio de transporte si aplica.');
    } elseif($tipo==='Cesantías'){
        $ces=round_money(($basePrest*$diasBase)/360); $add('Cesantías',$basePrest,$diasBase,0,$ces,0,'Base: salario + promedio variable + auxilio de transporte si aplica.');
    } elseif($tipo==='Intereses cesantías'){
        $ces=round_money(($basePrest*$diasBase)/360); $intCes=round_money($ces*0.12*$diasBase/360); $add('Cesantías base para intereses',$basePrest,$diasBase,0,$ces,0,'Valor base para cálculo de intereses.'); $add('Intereses sobre cesantías',$ces,$diasBase,0.12,$intCes,0,'12% anual proporcional por días.');
    } else {
        $prima=round_money(($basePrest*$diasBase)/360); $ces=round_money(($basePrest*$diasBase)/360); $intCes=round_money($ces*0.12*$diasBase/360); $diasParaVac=$diasPend>0?$diasPend:round_money($diasBase*15/360); $vac=round_money(($baseVac/30)*$diasParaVac);
        if($salPend>0) $add('Salarios pendientes',$salario,0,0,$salPend,0,'Valor manual de salarios pendientes.');
        $add('Prima de servicios',$basePrest,$diasBase,0,$prima,'0','Prestación proporcional.'); $add('Cesantías',$basePrest,$diasBase,0,$ces,0,'Cesantías proporcionales.'); $add('Intereses sobre cesantías',$ces,$diasBase,0.12,$intCes,0,'12% anual proporcional por días.'); $add('Vacaciones causadas no disfrutadas',$baseVac,$diasParaVac,0,$vac,0,'Vacaciones pendientes o proporcionales.'); if($dedManual>0) $add('Deducciones / descuentos autorizados',0,0,0,0,$dedManual,$detalleDesc?:'Valor manual de descuentos.');
    }
    if(!empty($input['variaciones']) && is_array($input['variaciones'])){ $det=[]; foreach($input['variaciones'] as $k=>$v){ if($v!==null && $v!=='') $det[]=$k.': '.money($v); } if($det){ $items[]=['concepto'=>'Promedio variaciones transitorias últimos 12 meses','base'=>0,'dias'=>0,'tasa'=>0,'valor'=>0,'deduccion'=>0,'neto'=>0,'observacion'=>'Promedio incluido en IBL: '.money($variable).' | '.implode(' * ',$det)]; } }
    $totalBruto=round_money(array_sum(array_map(fn($i)=>(float)$i['valor'],$items))); $totalDed=round_money(array_sum(array_map(fn($i)=>(float)$i['deduccion'],$items))); return ['dias_base'=>$diasBase,'dias_nomina'=>$diasNomina,'dias_calendario'=>$diasCalendario,'dias_vacaciones'=>$diasVac,'dias_pendientes_vacaciones'=>$diasPend,'ibl_prestaciones'=>$basePrest,'ibl_vacaciones'=>$baseVac,'valor_vacaciones'=>$vac,'valor_prima'=>$prima,'valor_cesantias'=>$ces,'valor_intereses_cesantias'=>$intCes,'valor_deducciones'=>$totalDed,'total_bruto'=>$totalBruto,'total_neto'=>round_money($totalBruto-$totalDed),'items'=>$items]; }
function liquidation_select_columns(){ return "l.*, c.nombre_completo, c.numero_documento, c.tipo_documento, c.area, c.cargo, co.nombre company_name, co.logo_ruta company_logo"; }
function liquidations(){ require_login(); $u=current_user(); $company=(int)($_GET['company_id']??0); $coll=(int)($_GET['collaborator_id']??0); $tipo=$_GET['tipo_liquidacion']??''; $sql='SELECT '.liquidation_select_columns().' FROM liquidations l JOIN collaborators c ON c.id=l.collaborator_id JOIN companies co ON co.id=l.company_id WHERE 1=1'; $params=[]; if(user_restricts_company()){ $sql.=' AND l.company_id=?'; $params[]=$u['company_id']; } elseif($company){ $sql.=' AND l.company_id=?'; $params[]=$company; } if($coll){ $sql.=' AND l.collaborator_id=?'; $params[]=$coll; } if($tipo){ $sql.=' AND l.tipo_liquidacion=?'; $params[]=$tipo; } $sql.=' ORDER BY l.fecha_calculo DESC,l.id DESC LIMIT 1000'; $st=db()->prepare($sql); $st->execute($params); $rows=$st->fetchAll(); header_html('Liquidaciones'); ?><div class="sticky-controls no-print"><div class="card"><form method="get" class="filters"><input type="hidden" name="page" value="liquidations"><div><label>Empresa</label><select id="companySelect" name="company_id" onchange="this.form.submit()"><option value="">Todas</option><?= company_options($company) ?></select></div><div><label>Colaborador</label><select id="collaboratorSelect" class="combo-collaborator" name="collaborator_id"><?= collaborator_options($company,$coll) ?></select></div><div><label>Tipo</label><select name="tipo_liquidacion"><?= option_list(liquidation_types(),$tipo,'Todos') ?></select></div><div><label>&nbsp;</label><button class="btn">Filtrar</button></div></form></div><div class="card compact-card"><a class="btn ok" href="<?= e(base_url('index.php?page=liquidation_form')) ?>">Nueva liquidación</a></div></div><div class="grid cols-4"><div class="kpi"><div class="num"><?= e(count($rows)) ?></div><div class="label">Liquidaciones</div></div><div class="kpi"><div class="num"><?= e(money(array_sum(array_map(fn($r)=>(float)$r['total_bruto'],$rows)))) ?></div><div class="label">Total bruto</div></div><div class="kpi"><div class="num"><?= e(money(array_sum(array_map(fn($r)=>(float)$r['valor_deducciones'],$rows)))) ?></div><div class="label">Deducciones</div></div><div class="kpi"><div class="num"><?= e(money(array_sum(array_map(fn($r)=>(float)$r['total_neto'],$rows)))) ?></div><div class="label">Total neto</div></div></div><div class="card"><div class="table-wrap results-table-wrap"><table class="sortable"><thead><tr><th>Fecha</th><th>Empresa</th><th>Colaborador</th><th>Documento</th><th>Tipo</th><th>Desde</th><th>Hasta</th><th>Días</th><th>Total neto</th><th>Estado</th><th class="no-sort no-print">Acciones</th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= e(dt($r['fecha_calculo'])) ?></td><td><?= e($r['company_name']) ?></td><td><?= e($r['nombre_completo']) ?></td><td><?= e($r['numero_documento']) ?></td><td><?= e($r['tipo_liquidacion']) ?></td><td><?= e(dt($r['fecha_inicio'])) ?></td><td><?= e(dt($r['fecha_fin'])) ?></td><td><?= e($r['dias_base']) ?></td><td><?= e(money($r['total_neto'])) ?></td><td><?= status_pill($r['estado']) ?></td><td class="no-print"><a href="<?= e(base_url('index.php?page=liquidation_view&id='.$r['id'])) ?>">Ver</a> · <a href="<?= e(base_url('index.php?page=liquidation_form&id='.$r['id'])) ?>">Editar</a> <?= delete_button('liquidation',$r['id'],'index.php?page=liquidations') ?></td></tr><?php endforeach; ?></tbody></table></div></div><?php footer_html(); }
function liquidation_form(){ require_write(); $u=current_user(); $id=(int)($_GET['id']??0); $prefColl=(int)($_GET['collaborator_id']??0); $prefCompany=(int)($_GET['company_id']??0); $r=['company_id'=>$prefCompany ?: ($u['company_id']??''),'collaborator_id'=>$prefColl,'tipo_liquidacion'=>'Vacaciones','fecha_calculo'=>date('Y-m-d'),'fecha_inicio'=>'','fecha_fin'=>date('Y-m-d'),'salario_base'=>'','salario_modalidad'=>'Fijo','promedio_variable'=>'0','auxilio_transporte'=>'0','dias_vacaciones'=>'','dias_pendientes_vacaciones'=>'','valor_salarios_pendientes'=>'0','valor_deducciones'=>'0','estado'=>'Borrador','observaciones'=>'','deduccion_vacaciones'=>1]; $params=[]; if($id){ $st=db()->prepare('SELECT * FROM liquidations WHERE id=?'); $st->execute([$id]); $r=$st->fetch() ?: $r; if(!can_read_company($r['company_id'])) exit('No autorizado'); $params=json_decode($r['parametros_json']??'{}',true) ?: []; $r['deduccion_vacaciones']=$params['deduccion_vacaciones']??0; } if($prefColl && !$id){ $st=db()->prepare('SELECT * FROM collaborators WHERE id=?'); $st->execute([$prefColl]); if($c=$st->fetch()){ $r['company_id']=$c['company_id']; $r['salario_base']=$c['salario_base']; $r['salario_modalidad']=$c['salario_modalidad']?:'Fijo'; $r['fecha_inicio']=$c['fecha_ingreso']; } } if($_SERVER['REQUEST_METHOD']==='POST'){ check_csrf(); $company_id=can_manage_all_companies()?(int)$_POST['company_id']:(int)($u['company_id'] ?: $_POST['company_id']); $coll=(int)($_POST['collaborator_id']??0); if(!$coll){ flash('err','Debe seleccionar colaborador.'); header('Location: '.base_url('index.php?page=liquidation_form')); exit; } $variaciones=[]; $sumVar=0; $countVar=0; for($i=1;$i<=12;$i++){ $v=normalize_money($_POST['var_m_'.$i] ?? ''); $variaciones['Mes '.$i]=$v; if($v!==null){ $sumVar += (float)$v; $countVar++; } } $promedioPost=normalize_money($_POST['promedio_variable']); if($countVar>0){ $promedioPost=round_money($sumVar/$countVar); } $input=['tipo_liquidacion'=>$_POST['tipo_liquidacion'],'fecha_inicio'=>dbdate($_POST['fecha_inicio']),'fecha_fin'=>dbdate($_POST['fecha_fin']),'salario_base'=>normalize_money($_POST['salario_base']),'salario_modalidad'=>$_POST['salario_modalidad'],'promedio_variable'=>$promedioPost,'auxilio_transporte'=>normalize_money($_POST['auxilio_transporte']),'dias_calendario'=>normalize_money($_POST['dias_calendario'] ?? ''),'dias_nomina'=>normalize_money($_POST['dias_nomina'] ?? ''),'detalle_dias'=>trim($_POST['detalle_dias'] ?? ''),'detalle_descuentos'=>trim($_POST['detalle_descuentos'] ?? ''),'variaciones'=>$variaciones,'dias_vacaciones'=>normalize_money($_POST['dias_vacaciones']),'dias_pendientes_vacaciones'=>normalize_money($_POST['dias_pendientes_vacaciones']),'valor_salarios_pendientes'=>normalize_money($_POST['valor_salarios_pendientes']),'valor_deducciones'=>normalize_money($_POST['valor_deducciones']),'deduccion_vacaciones'=>!empty($_POST['deduccion_vacaciones'])]; $calc=liquidation_compute($input); $paramsJson=json_encode($input,JSON_UNESCAPED_UNICODE); $vals=[$company_id,$coll,$_POST['tipo_liquidacion'],dbdate($_POST['fecha_calculo'])?:date('Y-m-d'),dbdate($_POST['fecha_inicio']),dbdate($_POST['fecha_fin']),normalize_money($_POST['salario_base']),$_POST['salario_modalidad'],$promedioPost,normalize_money($_POST['auxilio_transporte']),$calc['ibl_prestaciones'],$calc['ibl_vacaciones'],$calc['dias_base'],$calc['dias_vacaciones'],$calc['dias_pendientes_vacaciones'],$calc['valor_vacaciones'],$calc['valor_prima'],$calc['valor_cesantias'],$calc['valor_intereses_cesantias'],normalize_money($_POST['valor_salarios_pendientes']),$calc['valor_deducciones'],$calc['total_bruto'],$calc['total_neto'],$_POST['estado'],trim($_POST['observaciones']),$paramsJson]; if($id){ $vals[]=$u['id']; $vals[]=$id; db()->prepare('UPDATE liquidations SET company_id=?,collaborator_id=?,tipo_liquidacion=?,fecha_calculo=?,fecha_inicio=?,fecha_fin=?,salario_base=?,salario_modalidad=?,promedio_variable=?,auxilio_transporte=?,ibl_prestaciones=?,ibl_vacaciones=?,dias_base=?,dias_vacaciones=?,dias_pendientes_vacaciones=?,valor_vacaciones=?,valor_prima=?,valor_cesantias=?,valor_intereses_cesantias=?,valor_salarios_pendientes=?,valor_deducciones=?,total_bruto=?,total_neto=?,estado=?,observaciones=?,parametros_json=?,updated_by=? WHERE id=?')->execute($vals); db()->prepare('DELETE FROM liquidation_items WHERE liquidation_id=?')->execute([$id]); } else { $vals[]=$u['id']; db()->prepare('INSERT INTO liquidations(company_id,collaborator_id,tipo_liquidacion,fecha_calculo,fecha_inicio,fecha_fin,salario_base,salario_modalidad,promedio_variable,auxilio_transporte,ibl_prestaciones,ibl_vacaciones,dias_base,dias_vacaciones,dias_pendientes_vacaciones,valor_vacaciones,valor_prima,valor_cesantias,valor_intereses_cesantias,valor_salarios_pendientes,valor_deducciones,total_bruto,total_neto,estado,observaciones,parametros_json,created_by) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')->execute($vals); $id=db()->lastInsertId(); $tipoLiq=$_POST['tipo_liquidacion']; if($tipoLiq==='Vacaciones'){ $catLiq='Vacaciones'; } elseif($tipoLiq==='Prima de servicios'){ $catLiq='Liquidación prima de servicios'; } elseif(in_array($tipoLiq,['Cesantías','Intereses cesantías'],true)){ $catLiq='Liquidación cesantías y/o Int. Ces.'; } else { $catLiq='Liquidación de contrato'; } create_auto_novelty($company_id,$coll,$catLiq,'Liquidación - '.$_POST['tipo_liquidacion'],dbdate($_POST['fecha_calculo'])?:date('Y-m-d'),null,'Liquidación generada desde módulo de liquidaciones',$calc['total_neto']); } foreach($calc['items'] as $it){ db()->prepare('INSERT INTO liquidation_items(liquidation_id,concepto,base,dias,tasa,valor,deduccion,neto,observacion) VALUES(?,?,?,?,?,?,?,?,?)')->execute([$id,$it['concepto'],$it['base'],$it['dias'],$it['tasa'],$it['valor'],$it['deduccion'],$it['neto'],$it['observacion']]); } flash('ok','Liquidación guardada.'); header('Location: '.base_url('index.php?page=liquidation_view&id='.$id)); exit; } $sql='SELECT id,company_id,nombre_completo,numero_documento,salario_base,salario_modalidad,fecha_ingreso FROM collaborators WHERE 1=1'; $params=[]; if(user_restricts_company()){ $sql.=' AND company_id=?'; $params[]=$u['company_id']; } $sql.=' ORDER BY numero_documento,nombre_completo LIMIT 5000'; $st=db()->prepare($sql); $st->execute($params); $colls=$st->fetchAll(); header_html($id?'Editar liquidación':'Nueva liquidación'); ?><div class="card"><form method="post"><?= csrf_field() ?><div class="grid cols-3"><div class="field"><label>Empresa</label><select id="companySelect" name="company_id" <?= user_restricts_company()?'disabled':'' ?>><?= company_options($r['company_id']) ?></select></div><div class="field"><label>Colaborador</label><select id="collaboratorSelect" class="combo-collaborator" name="collaborator_id" required><option value="">Seleccione</option><?php foreach($colls as $c): ?><option data-company="<?= e($c['company_id']) ?>" data-salario="<?= e($c['salario_base']) ?>" data-salmod="<?= e($c['salario_modalidad']) ?>" data-ingreso="<?= e($c['fecha_ingreso']) ?>" value="<?= e($c['id']) ?>" <?= (string)$r['collaborator_id']===(string)$c['id']?'selected':'' ?>><?= e($c['numero_documento'].' - '.$c['nombre_completo']) ?></option><?php endforeach; ?></select></div><div class="field"><label>Tipo liquidación</label><select name="tipo_liquidacion"><?= option_list(liquidation_types(),$r['tipo_liquidacion']) ?></select></div><div class="field"><label>Fecha cálculo</label><input type="date" name="fecha_calculo" value="<?= e($r['fecha_calculo']) ?>"></div><div class="field"><label>Desde</label><input id="liqFechaInicio" type="date" name="fecha_inicio" value="<?= e($r['fecha_inicio']) ?>"></div><div class="field"><label>Hasta</label><input type="date" name="fecha_fin" value="<?= e($r['fecha_fin']) ?>"></div><div class="field"><label>Días calendario</label><input name="dias_calendario" value="<?= e($params['dias_calendario'] ?? '') ?>"><div class="hint">Detalle del formato de vacaciones.</div></div><div class="field"><label>Días nómina</label><input name="dias_nomina" value="<?= e($params['dias_nomina'] ?? '') ?>"><div class="hint">Días base tipo 360 para prestaciones.</div></div><div class="field"><label>Salario base</label><input id="liqSalario" name="salario_base" value="<?= e($r['salario_base']) ?>"></div><div class="field"><label>Salario mod.</label><select id="liqSalMod" name="salario_modalidad"><?= option_list(salario_mods(),$r['salario_modalidad']) ?></select></div><div class="field"><label>Promedio variable</label><input name="promedio_variable" value="<?= e($r['promedio_variable']) ?>"><div class="hint">Puede digitarse manualmente o calcularse con las 12 variaciones transitorias.</div></div><div class="field"><label>Auxilio transporte</label><input name="auxilio_transporte" value="<?= e($r['auxilio_transporte']) ?>"></div><div class="field"><label>Días vacaciones</label><input name="dias_vacaciones" value="<?= e($r['dias_vacaciones']) ?>"></div><div class="field"><label>Días vacaciones pendientes</label><input name="dias_pendientes_vacaciones" value="<?= e($r['dias_pendientes_vacaciones']) ?>"></div><div class="field"><label>Salarios pendientes</label><input name="valor_salarios_pendientes" value="<?= e($r['valor_salarios_pendientes']) ?>"></div><div class="field"><label>Deducciones manuales</label><input name="valor_deducciones" value="<?= e($r['valor_deducciones']) ?>"></div><div class="field"><label>Estado</label><select name="estado"><?= option_list(['Borrador','Aprobada','Pagada','Anulada'],$r['estado']) ?></select></div></div><div class="card subcard"><h3>Variaciones transitorias salariales últimos 12 meses</h3><p class="hint">Registre horas extra, recargos, comisiones u otros pagos salariales variables. Si diligencia uno o más meses, el sistema calcula el promedio y lo incorpora al IBL.</p><div class="grid cols-4"><?php $varSaved=$params['variaciones'] ?? []; for($i=1;$i<=12;$i++): $vv=$varSaved['Mes '.$i] ?? ''; ?><div class="field"><label>Mes <?= $i ?></label><input name="var_m_<?= $i ?>" value="<?= e($vv) ?>"></div><?php endfor; ?></div></div><div class="grid cols-2"><div class="field"><label>Detalle de días</label><textarea name="detalle_dias"><?= e($params['detalle_dias'] ?? '') ?></textarea><div class="hint">Ej.: días calendario, días nómina, periodos disfrutados, anticipados o compensados.</div></div><div class="field"><label>Detalle de valores a descontar</label><textarea name="detalle_descuentos"><?= e($params['detalle_descuentos'] ?? '') ?></textarea><div class="hint">Ej.: 8% vacaciones, préstamos, anticipos o descuentos autorizados.</div></div></div><div class="grid cols-3"><label class="checkline"><input type="checkbox" name="deduccion_vacaciones" value="1" <?= !empty($r['deduccion_vacaciones'])?'checked':'' ?>> Aplicar descuento 8% sobre vacaciones.</label><div class="field"><label>Observaciones</label><textarea name="observaciones"><?= e($r['observaciones']) ?></textarea></div><button class="btn">Calcular y guardar</button></form></div><?php footer_html(); }
function liquidation_view(){ require_login(); $id=(int)($_GET['id']??0); $st=db()->prepare('SELECT '.liquidation_select_columns().' FROM liquidations l JOIN collaborators c ON c.id=l.collaborator_id JOIN companies co ON co.id=l.company_id WHERE l.id=?'); $st->execute([$id]); $r=$st->fetch(); if(!$r||!can_read_company($r['company_id'])) exit('No autorizado'); $st=db()->prepare('SELECT * FROM liquidation_items WHERE liquidation_id=? ORDER BY id'); $st->execute([$id]); $items=$st->fetchAll(); $clientLogo=company_logo_url($r['company_id']); $liqParams=json_decode($r['parametros_json'] ?? '{}', true) ?: []; header_html('Detalle de liquidación'); ?><div class="card no-print"><div class="actions"><a class="btn" href="<?= e(base_url('index.php?page=liquidation_form&id='.$id)) ?>">Editar</a><button class="btn secondary" onclick="copyTable('tabla-liquidacion-detalle')">Copiar tabla</button><button class="btn secondary" onclick="printReport()">Imprimir / PDF</button><a class="btn secondary" href="<?= e(base_url('index.php?page=liquidations')) ?>">Volver</a></div></div><div id="report-print-area" class="card report-card"><div class="report-cover"><div class="report-brand"><img src="<?= e(mandatos_logo_url()) ?>"><div><div class="report-title">Liquidación - <?= e($r['tipo_liquidacion']) ?></div><div class="report-subtitle">Gestión Laboral y Novedades de Personal</div><div class="report-meta">Generado: <?= e(date('d/m/Y H:i')) ?></div></div></div><?php if($clientLogo): ?><div class="report-client"><span>Empresa cliente</span><img src="<?= e($clientLogo) ?>"></div><?php endif; ?></div><div class="liq-summary"><div><strong>Colaborador</strong><br><?= e($r['nombre_completo']) ?></div><div><strong>Documento</strong><br><?= e($r['tipo_documento'].' '.$r['numero_documento']) ?></div><div><strong>Empresa</strong><br><?= e($r['company_name']) ?></div><div><strong>Periodo</strong><br><?= e(dt($r['fecha_inicio']).' a '.dt($r['fecha_fin'])) ?></div><div><strong>Días base</strong><br><?= e($r['dias_base']) ?></div><div><strong>Estado</strong><br><?= status_pill($r['estado']) ?></div></div><div class="liq-summary detail"><div><strong>Días calendario</strong><br><?= e($liqParams['dias_calendario'] ?? '') ?></div><div><strong>Días nómina</strong><br><?= e($liqParams['dias_nomina'] ?? $r['dias_base']) ?></div><div><strong>Detalle días</strong><br><?= e($liqParams['detalle_dias'] ?? '') ?></div><div><strong>Detalle descuentos</strong><br><?= e($liqParams['detalle_descuentos'] ?? '') ?></div></div><?php if(!empty($liqParams['variaciones'])): ?><h3>Variaciones transitorias últimos 12 meses</h3><div class="table-wrap"><table class="sortable"><thead><tr><th>Mes</th><th>Valor salarial variable</th></tr></thead><tbody><?php foreach($liqParams['variaciones'] as $mes=>$valor): if($valor===null || $valor==='') continue; ?><tr><td><?= e($mes) ?></td><td><?= e(money($valor)) ?></td></tr><?php endforeach; ?></tbody></table></div><?php endif; ?><div class="grid cols-4"><div class="kpi"><div class="num"><?= e(money($r['ibl_prestaciones'])) ?></div><div class="label">IBL prestaciones</div></div><div class="kpi"><div class="num"><?= e(money($r['ibl_vacaciones'])) ?></div><div class="label">IBL vacaciones</div></div><div class="kpi"><div class="num"><?= e(money($r['total_bruto'])) ?></div><div class="label">Total bruto</div></div><div class="kpi"><div class="num"><?= e(money($r['total_neto'])) ?></div><div class="label">Total neto</div></div></div><h3>Detalle de conceptos</h3><div class="table-wrap"><table id="tabla-liquidacion-detalle" class="sortable"><thead><tr><th>Concepto</th><th>Base</th><th>Días</th><th>Tasa</th><th>Valor</th><th>Deducción</th><th>Neto</th><th>Observación</th></tr></thead><tbody><?php foreach($items as $it): ?><tr><td><?= e($it['concepto']) ?></td><td><?= e(money($it['base'])) ?></td><td><?= e($it['dias']) ?></td><td><?= e($it['tasa']) ?></td><td><?= e(money($it['valor'])) ?></td><td><?= e(money($it['deduccion'])) ?></td><td><?= e(money($it['neto'])) ?></td><td><?= e($it['observacion']) ?></td></tr><?php endforeach; ?></tbody></table></div><?php if($r['observaciones']): ?><div class="note-box"><strong>Observaciones:</strong> <?= e($r['observaciones']) ?></div><?php endif; ?><div class="legal-note">Cálculo de apoyo operativo. Validar antes de pago contra novedades, pagos previos, ausencias, salario variable, auxilio de transporte y descuentos autorizados.</div></div><?php footer_html(); }

function delete_record(){ require_write(); $type=$_GET['type'] ?? ''; $id=(int)($_GET['id'] ?? 0); $return=$_GET['return'] ?? 'index.php?page=dashboard'; $u=current_user();
    $map=[
        'company'=>['companies','company_id','index.php?page=companies'],
        'collaborator'=>['collaborators','company_id','index.php?page=collaborators'],
        'novelty'=>['novelties','company_id','index.php?page=novelties'],
        'liquidation'=>['liquidations','company_id','index.php?page=liquidations'],
        'user'=>['users','company_id','index.php?page=users']
    ];
    if(!isset($map[$type]) || !$id){ flash('err','Tipo de registro no válido.'); header('Location: '.base_url($return)); exit; }
    [$table,$companyCol,$defaultReturn]=$map[$type];
    if(in_array($type,['user','company'],true) && !is_super()){ exit('No autorizado'); }
    try{
        $row=null; $st=db()->prepare("SELECT * FROM `$table` WHERE id=? LIMIT 1"); $st->execute([$id]); $row=$st->fetch();
        if(!$row){ flash('err','El registro no existe.'); header('Location: '.base_url($return ?: $defaultReturn)); exit; }
        if(isset($row['company_id']) && !can_read_company($row['company_id'])) exit('No autorizado');
        db()->prepare("DELETE FROM `$table` WHERE id=? LIMIT 1")->execute([$id]);
        audit('Elimina registro',$table,$id,null,$row['company_id'] ?? null);
        flash('ok','Registro eliminado correctamente.');
    }catch(Throwable $e){ flash('err','No fue posible eliminar el registro. Puede tener historial o documentos asociados. Detalle: '.$e->getMessage()); }
    header('Location: '.base_url($return ?: $defaultReturn)); exit;
}

function palette_colors(){ return ['#01495E','#6DC2D5','#0A6177','#78D7E8','#0E849A','#BDEDF5','#013745','#3FAFC3','#8AD3E1','#2E6F7D','#9EE3EE','#005B70']; }
function chart_bar($title,$rows,$labelKey='label',$valueKey='total'){
    $max=0; foreach($rows as $r){ $max=max($max,(float)($r[$valueKey] ?? 0)); }
    echo '<div class="card chart-card chart-visual"><h3>'.e($title).'</h3>';
    if(!$rows){ echo '<p class="muted">Sin información para los filtros seleccionados.</p></div>'; return; }
    echo '<div class="bar-chart-visual">';
    foreach($rows as $i=>$r){
        $label=(string)($r[$labelKey] ?? 'Sin dato'); if($label==='') $label='Sin dato';
        $val=(float)($r[$valueKey] ?? 0); $pct=$max>0 ? max(3,round(($val/$max)*100,2)) : 0;
        echo '<div class="bar-row-visual" style="display:grid;grid-template-columns:minmax(150px,240px) 1fr 64px;gap:10px;align-items:center;margin:10px 0;">';
        echo '<div class="bar-label-visual" style="font-weight:800;color:#01495E;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'.e($label).'</div>';
        echo '<div class="bar-track-visual" style="height:18px;border-radius:999px;background:#E6F3F7;border:1px solid #D2E8EE;overflow:hidden;box-shadow:inset 0 1px 4px rgba(1,73,94,.08);"><div class="bar-fill-visual" style="height:100%;width:'.$pct.'%;border-radius:999px;background:linear-gradient(90deg,#01495E,#6DC2D5);box-shadow:0 4px 14px rgba(1,73,94,.25);"></div></div>';
        echo '<div class="bar-value-visual" style="text-align:right;font-weight:900;color:#01495E;">'.e(number_format($val,0,',','.')).'</div>';
        echo '</div>';
    }
    echo '</div></div>';
}
function chart_donut($title,$rows,$labelKey='label',$valueKey='total'){
    $total=0; foreach($rows as $r){ $total+=(float)($r[$valueKey] ?? 0); }
    $colors=palette_colors();
    echo '<div class="card chart-card chart-visual"><h3>'.e($title).'</h3>';
    if(!$rows || $total<=0){ echo '<p class="muted">Sin información para los filtros seleccionados.</p></div>'; return; }
    $cursor=0; $parts=[];
    foreach($rows as $i=>$r){
        $val=(float)($r[$valueKey] ?? 0); if($val<=0) continue;
        $deg=($val/$total)*360; $color=$colors[$i%count($colors)];
        $parts[]=$color.' '.round($cursor,2).'deg '.round($cursor+$deg,2).'deg'; $cursor+=$deg;
    }
    $gradient='conic-gradient('.implode(',',$parts).')';
    echo '<div class="donut-layout" style="display:flex;gap:18px;align-items:center;min-height:180px;">';
    echo '<div class="donut-chart" style="width:158px;height:158px;border-radius:50%;background:'.$gradient.';position:relative;box-shadow:0 12px 26px rgba(1,73,94,.15);flex:0 0 auto;"><div style="position:absolute;inset:34px;border-radius:50%;background:white;display:flex;align-items:center;justify-content:center;flex-direction:column;color:#01495E;font-weight:900;"><span style="font-size:24px;line-height:1;">'.e(number_format($total,0,',','.')).'</span><small style="font-size:11px;color:#627482;font-weight:800;">total</small></div></div>';
    echo '<div class="donut-legend" style="display:grid;gap:7px;flex:1;">';
    foreach($rows as $i=>$r){
        $label=(string)($r[$labelKey] ?? 'Sin dato'); if($label==='') $label='Sin dato';
        $val=(float)($r[$valueKey] ?? 0); $color=$colors[$i%count($colors)]; $p=$total>0?round(($val/$total)*100,1):0;
        echo '<div style="display:flex;align-items:center;gap:8px;font-weight:750;color:#18323D;"><span style="width:12px;height:12px;border-radius:4px;background:'.$color.';display:inline-block;flex:0 0 auto;"></span><span style="flex:1;">'.e($label).'</span><strong style="color:#01495E;">'.e(number_format($val,0,',','.')).'</strong><span style="color:#627482;font-size:12px;min-width:42px;text-align:right;">'.e($p).'%</span></div>';
    }
    echo '</div></div></div>';
}
function chart_dual_bar($title,$rows){
    echo '<div class="card chart-card chart-visual"><h3>'.e($title).'</h3>';
    if(!$rows){ echo '<p class="muted">Sin información para los filtros seleccionados.</p></div>'; return; }
    $max=0; foreach($rows as $r){ $max=max($max,(float)($r['ingresos'] ?? 0),(float)($r['retiros'] ?? 0)); }
    echo '<div class="dual-chart" style="display:grid;gap:12px;">';
    foreach($rows as $r){
        $ing=(float)($r['ingresos'] ?? 0); $ret=(float)($r['retiros'] ?? 0); $pi=$max>0?max(2,round($ing/$max*100,2)):0; $pr=$max>0?max(2,round($ret/$max*100,2)):0;
        echo '<div style="display:grid;grid-template-columns:90px 1fr 52px;gap:10px;align-items:center;"><div style="font-weight:850;color:#01495E;">'.e($r['label']).'</div><div style="display:grid;gap:5px;"><div title="Ingresos" style="height:13px;border-radius:999px;background:#E7F6F9;overflow:hidden;"><div style="height:100%;width:'.$pi.'%;background:linear-gradient(90deg,#087443,#6DC2D5);border-radius:999px;"></div></div><div title="Retiros" style="height:13px;border-radius:999px;background:#F7EAEA;overflow:hidden;"><div style="height:100%;width:'.$pr.'%;background:linear-gradient(90deg,#B42318,#F97066);border-radius:999px;"></div></div></div><div style="font-size:12px;color:#01495E;font-weight:850;text-align:right;">I '.e(number_format($ing,0,',','.')).'<br>R '.e(number_format($ret,0,',','.')).'</div></div>';
    }
    echo '<div class="hint"><span style="display:inline-block;width:10px;height:10px;background:#087443;border-radius:3px;"></span> Ingresos &nbsp; <span style="display:inline-block;width:10px;height:10px;background:#B42318;border-radius:3px;"></span> Retiros</div>';
    echo '</div></div>';
}
function pct($part,$total){ return $total>0 ? round(($part/$total)*100,1) : 0; }


function indicators(){ require_login(); $u=current_user();
    $company=(int)($_GET['company_id'] ?? 0); $coll=(int)($_GET['collaborator_id'] ?? 0); $estado=$_GET['estado'] ?? ''; $tipoContrato=$_GET['tipo_vinculacion'] ?? ''; $salarioMod=$_GET['salario_modalidad'] ?? ''; $categoria=$_GET['categoria'] ?? ''; $fi=$_GET['fi'] ?? ''; $ff=$_GET['ff'] ?? ''; $quincena=$_GET['quincena'] ?? '';
    $cWhere=' WHERE 1=1'; $cParams=[]; $nWhere=' WHERE 1=1'; $nParams=[];
    if(user_restricts_company()){ $cWhere.=' AND c.company_id=?'; $cParams[]=$u['company_id']; $nWhere.=' AND n.company_id=?'; $nParams[]=$u['company_id']; }
    elseif($company){ $cWhere.=' AND c.company_id=?'; $cParams[]=$company; $nWhere.=' AND n.company_id=?'; $nParams[]=$company; }
    if($coll){ $cWhere.=' AND c.id=?'; $cParams[]=$coll; $nWhere.=' AND n.collaborator_id=?'; $nParams[]=$coll; }
    if($estado){ $cWhere.=' AND c.estado=?'; $cParams[]=$estado; }
    if($tipoContrato){ $cWhere.=' AND c.tipo_vinculacion=?'; $cParams[]=$tipoContrato; }
    if($salarioMod){ $cWhere.=' AND c.salario_modalidad=?'; $cParams[]=$salarioMod; }
    $nWhere.=category_filter_sql('n',$categoria,$nParams);
    if($fi){ $nWhere.=' AND COALESCE(n.fecha_inicio,n.fecha_registro)>=?'; $nParams[]=$fi; }
    if($ff){ $nWhere.=' AND COALESCE(n.fecha_inicio,n.fecha_registro)<=?'; $nParams[]=$ff; }
    if($quincena){ $nWhere.=' AND n.quincena=?'; $nParams[]=$quincena; }
    $one=function($sql,$params){ $st=db()->prepare($sql); $st->execute($params); return (int)$st->fetchColumn(); };
    $all=function($sql,$params){ $st=db()->prepare($sql); $st->execute($params); return $st->fetchAll(); };
    $totalCol=$one('SELECT COUNT(*) FROM collaborators c'.$cWhere,$cParams);
    $activos=$one('SELECT COUNT(*) FROM collaborators c'.$cWhere.' AND c.estado="Activo"',$cParams);
    $retirados=$one('SELECT COUNT(*) FROM collaborators c'.$cWhere.' AND c.estado="Retirado"',$cParams);
    $novTotal=$one('SELECT COUNT(*) FROM novelties n'.$nWhere,$nParams);
    $colEstado=$all('SELECT COALESCE(NULLIF(c.estado,""),"Sin dato") label, COUNT(*) total FROM collaborators c'.$cWhere.' GROUP BY label ORDER BY total DESC',$cParams);
    $colTipo=$all('SELECT COALESCE(NULLIF(c.tipo_vinculacion,""),"Sin dato") label, COUNT(*) total FROM collaborators c'.$cWhere.' GROUP BY label ORDER BY total DESC',$cParams);
    $colSalario=$all('SELECT COALESCE(NULLIF(c.salario_modalidad,""),"Sin dato") label, COUNT(*) total FROM collaborators c'.$cWhere.' GROUP BY label ORDER BY total DESC',$cParams);
    $novCat=$all('SELECT COALESCE(NULLIF(n.categoria,""),"Sin dato") label, COUNT(*) total FROM novelties n'.$nWhere.' GROUP BY label ORDER BY total DESC LIMIT 12',$nParams);
    $novQuincena=$all('SELECT COALESCE(NULLIF(n.quincena,""),"Sin dato") label, COUNT(*) total FROM novelties n'.$nWhere.' GROUP BY label ORDER BY label DESC LIMIT 12',$nParams);
    $novTipo=$all('SELECT COALESCE(NULLIF(n.tipo_novedad,""),"Sin dato") label, COUNT(*) total FROM novelties n'.$nWhere.' GROUP BY label ORDER BY total DESC LIMIT 12',$nParams);
    $monthly=$all('SELECT DATE_FORMAT(COALESCE(n.fecha_inicio,n.fecha_registro), "%Y-%m") label, COUNT(*) total FROM novelties n'.$nWhere.' AND COALESCE(n.fecha_inicio,n.fecha_registro) IS NOT NULL GROUP BY label ORDER BY label DESC LIMIT 12',$nParams);
    $mov=$all('SELECT DATE_FORMAT(COALESCE(n.fecha_inicio,n.fecha_registro), "%Y-%m") label, SUM(CASE WHEN n.tipo_novedad LIKE "Ingreso%" OR n.categoria="Ingreso" THEN 1 ELSE 0 END) ingresos, SUM(CASE WHEN n.tipo_novedad LIKE "Retiro%" OR n.categoria="Retiro" THEN 1 ELSE 0 END) retiros FROM novelties n'.$nWhere.' AND COALESCE(n.fecha_inicio,n.fecha_registro) IS NOT NULL GROUP BY label ORDER BY label DESC LIMIT 12',$nParams);
    header_html('Indicadores'); ?>
    <div class="sticky-controls no-print"><div class="card"><form method="get" class="filters"><input type="hidden" name="page" value="indicators"><div><label>Empresa</label><select id="companySelect" name="company_id"><option value="">Todas</option><?= company_options($company) ?></select></div><div><label>Colaborador</label><select id="collaboratorSelect" class="combo-collaborator" name="collaborator_id"><?= collaborator_options($company,$coll) ?></select></div><div><label>Estado colaborador</label><select name="estado"><?= option_list(estados_colaborador(),$estado,'Todos') ?></select></div><div><label>Tipo de contrato</label><select name="tipo_vinculacion"><?= option_list(tipos_contrato(),$tipoContrato,'Todos') ?></select></div><div><label>Salario mod.</label><select name="salario_modalidad"><?= option_list(salario_mods(),$salarioMod,'Todos') ?></select></div><div><label>Categoría novedad</label><select name="categoria"><?= option_list(categories(),$categoria,'Todas') ?></select></div><div><label>Fecha inicial novedad</label><input type="date" name="fi" value="<?= e($fi) ?>"></div><div><label>Fecha final novedad</label><input type="date" name="ff" value="<?= e($ff) ?>"></div><div><label>Quincena</label><input name="quincena" value="<?= e($quincena) ?>" placeholder="2026-06-Q1"></div><div><label>&nbsp;</label><button class="btn" type="submit">Actualizar indicadores</button></div></form></div></div>
    <div class="grid cols-4"><div class="kpi"><div class="num"><?= e($totalCol) ?></div><div class="label">Colaboradores filtrados</div></div><div class="kpi"><div class="num"><?= e($activos) ?></div><div class="label">Activos <?= e(pct($activos,$totalCol)) ?>%</div></div><div class="kpi"><div class="num"><?= e($retirados) ?></div><div class="label">Retirados <?= e(pct($retirados,$totalCol)) ?>%</div></div><div class="kpi"><div class="num"><?= e($novTotal) ?></div><div class="label">Novedades filtradas</div></div></div>
    <div class="grid cols-2"><?php chart_donut('Colaboradores por estado',$colEstado); chart_donut('Colaboradores por tipo de contrato',$colTipo); chart_donut('Salario fijo / variable',$colSalario); chart_bar('Novedades por categoría',$novCat); chart_bar('Novedades por quincena',$novQuincena); chart_bar('Top tipos de novedad',$novTipo); chart_bar('Novedades por mes',$monthly); chart_dual_bar('Ingresos y retiros por mes',$mov); ?></div>
    <?php footer_html(); }

$page=$_GET['page'] ?? 'dashboard';
$routes=['login'=>'login','logout'=>'logout','dashboard'=>'dashboard','companies'=>'companies','company_form'=>'company_form','collaborators'=>'collaborators','collaborator_form'=>'collaborator_form','collaborator_view'=>'collaborator_view','novelties'=>'novelties','novelty_form'=>'novelty_form','reports'=>'reports','indicators'=>'indicators','delete_record'=>'delete_record','import'=>'import_page','users'=>'users','user_form'=>'user_form','audit'=>'audit_page','liquidations'=>'liquidations','liquidation_form'=>'liquidation_form','liquidation_view'=>'liquidation_view'];
if(!isset($routes[$page])) $page='dashboard';
$routes[$page]();
