<?php
/**
* Clase de utilidades para Working English System
*/
if (!defined('ABSPATH')) {
exit;
}
class WES_Utils {
/**
* Generar código único para estudiante
*/
public static function generate_student_code($branch_id = null) {
global $wpdb;
// Obtener código de sede
$branch_code = 'MAIN';
if ($branch_id) {
$branch_code = $wpdb->get_var($wpdb->prepare(
"SELECT code FROM {$wpdb->prefix}wes_branches WHERE id = %d",
$branch_id
));
}
// Año actual
$year = date('Y');
// Obtener el siguiente número secuencial
$last_number = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(CAST(SUBSTRING(student_code, -4) AS UNSIGNED))
FROM {$wpdb->prefix}wes_students
WHERE student_code LIKE %s",
$branch_code . $year . '%'
));
$next_number = str_pad(($last_number + 1), 4, '0', STR_PAD_LEFT);
return $branch_code . $year . $next_number;
}
/**
* Generar código único para grupo
*/
public static function generate_group_code($language_id, $program_id, $level_id, $year = null) {
global $wpdb;
if (!$year) {
$year = date('Y');
}
// Obtener códigos
$language_code = $wpdb->get_var($wpdb->prepare(
"SELECT code FROM {$wpdb->prefix}wes_languages WHERE id = %d",
$language_id
));
$program_code = $wpdb->get_var($wpdb->prepare(
"SELECT code FROM {$wpdb->prefix}wes_programs WHERE id = %d",
$program_id
));
$level_code = $wpdb->get_var($wpdb->prepare(
"SELECT code FROM {$wpdb->prefix}wes_levels WHERE id = %d",
$level_id
));
// Formato: EN-GE-A1-2024-001
$base_code = $language_code . '-' . $program_code . '-' . $level_code . '-' . $year;
// Obtener siguiente número correlativo
$last_number = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(CAST(SUBSTRING(code, -3) AS UNSIGNED))
FROM {$wpdb->prefix}wes_groups
WHERE code LIKE %s",
$base_code . '%'
));
$next_number = str_pad(($last_number + 1), 3, '0', STR_PAD_LEFT);
return $base_code . '-' . $next_number;
}
/**
* Generar número de recibo
*/
public static function generate_receipt_number() {
global $wpdb;
$year = date('Y');
$month = date('m');
// Formato: REC-202501-0001
$prefix = 'REC-' . $year . $month . '-';
$last_number = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(CAST(SUBSTRING(receipt_number, -4) AS UNSIGNED))
FROM {$wpdb->prefix}wes_student_payments
WHERE receipt_number LIKE %s",
$prefix . '%'
));
$next_number = str_pad(($last_number + 1), 4, '0', STR_PAD_LEFT);
return $prefix . $next_number;
}
/**
* Calcular cuotas prorrateadas
*/
public static function calculate_prorated_fees($start_date, $end_date, $monthly_fee, $modality = 'presencial') {
$start = new DateTime($start_date);
$end = new DateTime($end_date);
// Días de clase por modalidad
$class_days_per_week = array(
'presencial' => 5, // Lunes a viernes
'virtual' => 3, // 3 días por semana
'hibrida' => 4 // 4 días por semana
);
$days_per_week = isset($class_days_per_week[$modality]) ?
$class_days_per_week[$modality] : 5;
// Calcular días hábiles totales
$total_class_days = self::calculate_business_days($start, $end, $days_per_week);
// Días en el mes
$days_in_month = $start->format('t');
$business_days_in_month = ($days_in_month / 7) * $days_per_week;
// Calcular proporción
$daily_rate = $monthly_fee / $business_days_in_month;
$prorated_amount = $daily_rate * $total_class_days;
return round($prorated_amount, 2);
}
/**
* Calcular días hábiles entre fechas
*/
public static function calculate_business_days($start_date, $end_date, $days_per_week = 5) {
if (is_string($start_date)) {
$start_date = new DateTime($start_date);
}
if (is_string($end_date)) {
$end_date = new DateTime($end_date);
}
$interval = $start_date->diff($end_date);
$total_days = $interval->days;
$weeks = floor($total_days / 7);
$remaining_days = $total_days % 7;
// Calcular días de clase
$class_days = $weeks * $days_per_week;
// Agregar días restantes (simplificado)
$class_days += min($remaining_days, $days_per_week);
return $class_days;
}
/**
* Validar email
*/
public static function validate_email($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
/**
* Validar teléfono
*/
public static function validate_phone($phone) {
// Remover espacios y caracteres especiales
$phone = preg_replace('/[^0-9+]/', '', $phone);
// Debe tener al menos 8 dígitos
return strlen($phone) >= 8;
}
/**
* Formatear nombre propio
*/
public static function format_name($name) {
return ucwords(strtolower(trim($name)));
}
/**
* Sanitizar código
*/
public static function sanitize_code($code) {
return strtoupper(preg_replace('/[^A-Z0-9-]/', '', $code));
}
/**
* Obtener estado del estudiante con badge HTML
*/
public static function get_student_status_badge($status) {
$badges = array(
'active' => '<span class="badge bg-success">Activo</span>',
'inactive' => '<span class="badge bg-secondary">Inactivo</span>',
'graduated' => '<span class="badge bg-primary">Graduado</span>',
'suspended' => '<span class="badge bg-danger">Suspendido</span>'
);
return isset($badges[$status]) ? $badges[$status] : '<span class="badge bg-warning">Desconocido</span>';
}
/**
* Obtener estado del grupo con badge HTML
*/
public static function get_group_status_badge($status) {
$badges = array(
'active' => '<span class="badge bg-success">Activo</span>',
'inactive' => '<span class="badge bg-secondary">Inactivo</span>',
'completed' => '<span class="badge bg-primary">Completado</span>',
'cancelled' => '<span class="badge bg-danger">Cancelado</span>'
);
return isset($badges[$status]) ? $badges[$status] : '<span class="badge bg-warning">Desconocido</span>';
}
/**
* Obtener estado del pago con badge HTML
*/
public static function get_payment_status_badge($status) {
$badges = array(
'pending' => '<span class="badge bg-warning">Pendiente</span>',
'partial' => '<span class="badge bg-info">Parcial</span>',
'paid' => '<span class="badge bg-success">Pagado</span>',
'overdue' => '<span class="badge bg-danger">Vencido</span>',
'cancelled' => '<span class="badge bg-secondary">Cancelado</span>'
);
return isset($badges[$status]) ? $badges[$status] : '<span class="badge bg-dark">Desconocido</span>';
}
/**
* Generar paginación HTML
*/
public static function generate_pagination($current_page, $total_pages, $base_url) {
if ($total_pages <= 1) {
return '';
}
$pagination = '<nav aria-label="Paginación"><ul class="pagination justify-content-center">';
// Botón anterior
if ($current_page > 1) {
$prev_url = add_query_arg('paged', $current_page - 1, $base_url);
$pagination .= '<li class="page-item"><a class="page-link" href="' . esc_url($prev_url) . '">« Anterior</a></li>';
}
// Números de página
$start = max(1, $current_page - 2);
$end = min($total_pages, $current_page + 2);
for ($i = $start; $i <= $end; $i++) {
$active = ($i == $current_page) ? ' active' : '';
$page_url = add_query_arg('paged', $i, $base_url);
$pagination .= '<li class="page-item' . $active . '"><a class="page-link" href="' . esc_url($page_url) . '">' . $i . '</a></li>';
}
// Botón siguiente
if ($current_page < $total_pages) {
$next_url = add_query_arg('paged', $current_page + 1, $base_url);
$pagination .= '<li class="page-item"><a class="page-link" href="' . esc_url($next_url) . '">Siguiente »</a></li>';
}
$pagination .= '</ul></nav>';
return $pagination;
}
/**
* Exportar datos a CSV
*/
public static function export_to_csv($data, $filename, $headers = array()) {
if (empty($data)) {
return false;
}
// Headers para descarga
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
$output = fopen('php://output', 'w');
// BOM para UTF-8
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
// Headers si se proporcionan
if (!empty($headers)) {
fputcsv($output, $headers);
} else {
// Usar las claves del primer elemento como headers
$first_row = reset($data);
if (is_array($first_row) || is_object($first_row)) {
$headers = is_object($first_row) ? array_keys(get_object_vars($first_row)) : array_keys($first_row);
fputcsv($output, $headers);
}
}
// Datos
foreach ($data as $row) {
if (is_object($row)) {
$row = get_object_vars($row);
}
fputcsv($output, $row);
}
fclose($output);
exit;
}
/**
* Limpiar y validar datos de entrada
*/
public static function sanitize_input($data, $type = 'text') {
switch ($type) {
case 'email':
return sanitize_email($data);
case 'url':
return esc_url_raw($data);
case 'int':
return intval($data);
case 'float':
return floatval($data);
case 'textarea':
return sanitize_textarea_field($data);
case 'html':
return wp_kses_post($data);
default:
return sanitize_text_field($data);
}
}
/**
* Verificar si una fecha es válida
*/
public static function validate_date($date, $format = 'Y-m-d') {
$d = DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}
/**
* Calcular edad a partir de fecha de nacimiento
*/
public static function calculate_age($birth_date) {
if (!self::validate_date($birth_date)) {
return null;
}
$birth = new DateTime($birth_date);
$today = new DateTime();
$age = $today->diff($birth);
return $age->y;
}
/**
* Logging personalizado
*/
public static function log($message, $level = 'info') {
if (!defined('WP_DEBUG') || !WP_DEBUG) {
return;
}
$log_entry = '[' . date('Y-m-d H:i:s') . '] [WES] [' . strtoupper($level) . '] ' . $message . PHP_EOL;
$log_file = WP_CONTENT_DIR . '/wes-debug.log';
file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX);
}
/**
* Obtener configuración con valor por defecto
*/
public static function get_option($option_name, $default = '') {
return get_option('wes_' . $option_name, $default);
}
/**
* Guardar configuración
*/
public static function update_option($option_name, $value) {
return update_option('wes_' . $option_name, $value);
}
}