All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 53s
- Actualizada query de contadores con total_pagado_planillas y total_precios_tareas - Agregados dos nuevos cards en TotalesEmpleados para mostrar totales monetarios - Incluida diferencia entre pagos de planillas y precios de tareas - Agregada función formatCurrency para formateo monetario - Actualizada función copiarTexto con sección de totales monetarios - Agregada altura máxima (max-h-96) a tabla de planillas con scroll vertical - Cambiado grid de 4 a 3 columnas en lg para mejor distribución
254 lines
9.5 KiB
Vue
254 lines
9.5 KiB
Vue
<template>
|
|
<UCard class="brand-card border border-transparent">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-xl font-bold brand-section-title">Detalle de Planillas</h2>
|
|
<div class="flex items-center gap-2">
|
|
<UBadge color="gray" variant="soft" size="sm">
|
|
{{ data.length }} planillas
|
|
</UBadge>
|
|
<UButton
|
|
size="xs"
|
|
color="gray"
|
|
variant="soft"
|
|
icon="i-lucide-copy"
|
|
@click="copiarTexto"
|
|
>
|
|
Copiar Texto
|
|
</UButton>
|
|
<UButton
|
|
size="xs"
|
|
color="gray"
|
|
variant="soft"
|
|
icon="i-lucide-braces"
|
|
@click="copiarJSON"
|
|
>
|
|
Copiar JSON
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="overflow-x-auto max-h-96 overflow-y-auto">
|
|
<table class="w-full text-sm">
|
|
<thead>
|
|
<tr class="border-b border-[var(--brand-border)]">
|
|
<th class="text-left py-3 px-3 font-semibold text-[var(--brand-text)]">Empleado</th>
|
|
<th class="text-left py-3 px-3 font-semibold text-[var(--brand-text)]">Cédula</th>
|
|
<th class="text-left py-3 px-3 font-semibold text-[var(--brand-text)]">Título</th>
|
|
<th class="text-center py-3 px-3 font-semibold text-[var(--brand-text)]">Tareas</th>
|
|
<th class="text-right py-3 px-3 font-semibold text-[var(--brand-text)]">Suma Precios Tareas</th>
|
|
<th class="text-right py-3 px-3 font-semibold text-[var(--brand-text)]">Total Pagado</th>
|
|
<th class="text-right py-3 px-3 font-semibold text-[var(--brand-text)]">Diferencia</th>
|
|
<th class="text-center py-3 px-3 font-semibold text-[var(--brand-text)]">Periodo</th>
|
|
<th class="text-center py-3 px-3 font-semibold text-[var(--brand-text)]">Estado</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr
|
|
v-for="planilla in data"
|
|
:key="planilla.planilla_id"
|
|
class="border-b border-[var(--brand-border)] hover:bg-[var(--brand-surface)] transition-colors"
|
|
>
|
|
<td class="py-3 px-3 text-[var(--brand-text)]">
|
|
{{ planilla.empleado_nombre }}
|
|
</td>
|
|
<td class="py-3 px-3 text-[var(--brand-text-muted)] font-mono text-xs">
|
|
{{ planilla.empleado_cedula || 'N/A' }}
|
|
</td>
|
|
<td class="py-3 px-3 text-[var(--brand-text)]">
|
|
<span class="font-medium">{{ planilla.planilla_titulo }}</span>
|
|
</td>
|
|
<td class="py-3 px-3 text-center">
|
|
<UBadge color="purple" variant="soft" size="xs">
|
|
{{ planilla.cantidad_tareas }}
|
|
</UBadge>
|
|
</td>
|
|
<td class="py-3 px-3 text-right font-mono text-blue-400">
|
|
{{ formatCurrency(planilla.suma_precios_tareas || 0) }}
|
|
</td>
|
|
<td class="py-3 px-3 text-right font-mono text-green-400">
|
|
{{ formatCurrency(planilla.planilla_total || 0) }}
|
|
</td>
|
|
<td class="py-3 px-3 text-right">
|
|
<span
|
|
:class="{
|
|
'text-green-400': getDiferencia(planilla) > 0,
|
|
'text-red-400': getDiferencia(planilla) < 0,
|
|
'text-[var(--brand-text-muted)]': getDiferencia(planilla) === 0
|
|
}"
|
|
class="font-mono font-semibold"
|
|
>
|
|
{{ getDiferencia(planilla) > 0 ? '+' : '' }}{{ formatCurrency(getDiferencia(planilla)) }}
|
|
</span>
|
|
<div v-if="getDiferencia(planilla) !== 0" class="text-xs text-[var(--brand-text-muted)] mt-1">
|
|
{{ getDiferenciaPorcentaje(planilla) }}
|
|
</div>
|
|
</td>
|
|
<td class="py-3 px-3 text-center text-xs text-[var(--brand-text-muted)]">
|
|
{{ formatDate(planilla.planilla_fecha_desde) }}<br />
|
|
<span class="text-[10px]">al</span><br />
|
|
{{ formatDate(planilla.planilla_fecha_hasta) }}
|
|
</td>
|
|
<td class="py-3 px-3 text-center">
|
|
<UBadge
|
|
:color="planilla.planilla_estado === 'pagado' ? 'green' : 'orange'"
|
|
variant="soft"
|
|
size="xs"
|
|
>
|
|
{{ planilla.planilla_estado || 'pendiente' }}
|
|
</UBadge>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot v-if="data.length > 0" class="border-t-2 border-[var(--brand-border)]">
|
|
<tr class="bg-[var(--brand-surface)]">
|
|
<td colspan="3" class="py-3 px-3 font-semibold text-[var(--brand-text)]">
|
|
TOTALES
|
|
</td>
|
|
<td class="py-3 px-3 text-center">
|
|
<UBadge color="purple" variant="soft" size="sm">
|
|
{{ totalTareas }}
|
|
</UBadge>
|
|
</td>
|
|
<td class="py-3 px-3 text-right font-mono font-bold text-blue-400">
|
|
{{ formatCurrency(totalSumaPrecios) }}
|
|
</td>
|
|
<td class="py-3 px-3 text-right font-mono font-bold text-green-400">
|
|
{{ formatCurrency(totalPagado) }}
|
|
</td>
|
|
<td class="py-3 px-3 text-right">
|
|
<span
|
|
:class="{
|
|
'text-green-400': diferenciaTotal > 0,
|
|
'text-red-400': diferenciaTotal < 0,
|
|
'text-[var(--brand-text-muted)]': diferenciaTotal === 0
|
|
}"
|
|
class="font-mono font-bold"
|
|
>
|
|
{{ diferenciaTotal > 0 ? '+' : '' }}{{ formatCurrency(diferenciaTotal) }}
|
|
</span>
|
|
</td>
|
|
<td colspan="2"></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
|
|
<div v-if="data.length === 0" class="text-center py-12 text-[var(--brand-text-muted)]">
|
|
<div class="i-lucide-inbox text-4xl mx-auto mb-3 opacity-50"></div>
|
|
<p>No hay planillas en el rango de fechas seleccionado</p>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
interface Planilla {
|
|
planilla_id: number
|
|
empleado_id: number
|
|
empleado_nombre: string
|
|
empleado_cedula: string
|
|
planilla_titulo: string
|
|
planilla_total: number
|
|
planilla_fecha_desde: string
|
|
planilla_fecha_hasta: string
|
|
planilla_estado: string
|
|
suma_precios_tareas: number
|
|
cantidad_tareas: number
|
|
}
|
|
|
|
const props = defineProps<{
|
|
data: Planilla[]
|
|
rangoLegible: string
|
|
lastUpdated: string
|
|
}>()
|
|
|
|
// Computed para totales
|
|
const totalTareas = computed(() => {
|
|
return props.data.reduce((sum, p) => sum + (p.cantidad_tareas || 0), 0)
|
|
})
|
|
|
|
const totalSumaPrecios = computed(() => {
|
|
return props.data.reduce((sum, p) => sum + (p.suma_precios_tareas || 0), 0)
|
|
})
|
|
|
|
const totalPagado = computed(() => {
|
|
return props.data.reduce((sum, p) => sum + (p.planilla_total || 0), 0)
|
|
})
|
|
|
|
const diferenciaTotal = computed(() => {
|
|
return totalPagado.value - totalSumaPrecios.value
|
|
})
|
|
|
|
// Funciones helper
|
|
const getDiferencia = (planilla: Planilla) => {
|
|
return (planilla.planilla_total || 0) - (planilla.suma_precios_tareas || 0)
|
|
}
|
|
|
|
const getDiferenciaPorcentaje = (planilla: Planilla) => {
|
|
const suma = planilla.suma_precios_tareas || 0
|
|
if (suma === 0) return ''
|
|
const diff = getDiferencia(planilla)
|
|
const porcentaje = (diff / suma) * 100
|
|
return `${porcentaje > 0 ? '+' : ''}${porcentaje.toFixed(1)}%`
|
|
}
|
|
|
|
const formatCurrency = (value: number) => {
|
|
return new Intl.NumberFormat('es-HN', {
|
|
style: 'currency',
|
|
currency: 'HNL',
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2
|
|
}).format(value)
|
|
}
|
|
|
|
const formatDate = (dateString: string) => {
|
|
if (!dateString) return 'N/A'
|
|
const date = new Date(dateString)
|
|
return new Intl.DateTimeFormat('es-HN', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
}).format(date)
|
|
}
|
|
|
|
async function copiarTexto() {
|
|
const footer = `
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
📊 RESUMEN
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
📅 Rango: ${props.rangoLegible}
|
|
📋 Planillas: ${props.data.length}
|
|
🕐 Generado: ${props.lastUpdated}`
|
|
|
|
let texto = `📋 DETALLE DE PLANILLAS\n\n`
|
|
|
|
props.data.forEach((planilla, idx) => {
|
|
const diff = getDiferencia(planilla)
|
|
texto += `${idx + 1}. ${planilla.empleado_nombre} (${planilla.empleado_cedula || 'N/A'})\n`
|
|
texto += ` Planilla: ${planilla.planilla_titulo}\n`
|
|
texto += ` Periodo: ${formatDate(planilla.planilla_fecha_desde)} al ${formatDate(planilla.planilla_fecha_hasta)}\n`
|
|
texto += ` Tareas: ${planilla.cantidad_tareas}\n`
|
|
texto += ` Suma Precios Tareas: ${formatCurrency(planilla.suma_precios_tareas || 0)}\n`
|
|
texto += ` Total Pagado: ${formatCurrency(planilla.planilla_total || 0)}\n`
|
|
texto += ` Diferencia: ${diff > 0 ? '+' : ''}${formatCurrency(diff)} ${getDiferenciaPorcentaje(planilla)}\n`
|
|
texto += ` Estado: ${planilla.planilla_estado || 'pendiente'}\n\n`
|
|
})
|
|
|
|
texto += `\n📊 TOTALES GENERALES:\n`
|
|
texto += ` Total Tareas: ${totalTareas.value}\n`
|
|
texto += ` Total Suma Precios: ${formatCurrency(totalSumaPrecios.value)}\n`
|
|
texto += ` Total Pagado: ${formatCurrency(totalPagado.value)}\n`
|
|
texto += ` Diferencia Total: ${diferenciaTotal.value > 0 ? '+' : ''}${formatCurrency(diferenciaTotal.value)}${footer}`
|
|
|
|
await navigator.clipboard.writeText(texto)
|
|
alert('✅ Detalle de Planillas copiado al portapapeles')
|
|
}
|
|
|
|
async function copiarJSON() {
|
|
const json = JSON.stringify(props.data, null, 2)
|
|
await navigator.clipboard.writeText(json)
|
|
alert('✅ JSON copiado al portapapeles')
|
|
}
|
|
</script>
|