237 lines
11 KiB
Vue
237 lines
11 KiB
Vue
<script setup>
|
|
import { onMounted, ref } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import cashRegisterService from '@Services/cashRegisterService';
|
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
|
import SearcherHead from '@Holos/Searcher.vue';
|
|
import Table from '@Holos/Table.vue';
|
|
|
|
/** Router */
|
|
const router = useRouter();
|
|
|
|
/** Estado */
|
|
const cashRegisters = ref({
|
|
data: [],
|
|
total: 0
|
|
});
|
|
const loading = ref(false);
|
|
const searchQuery = ref('');
|
|
|
|
/** Métodos */
|
|
const loadHistory = async (query = '') => {
|
|
loading.value = true;
|
|
try {
|
|
const response = await cashRegisterService.getCashRegisterHistory({
|
|
search: query
|
|
});
|
|
|
|
// El servicio devuelve el objeto de paginación completo
|
|
if (response && response.data) {
|
|
cashRegisters.value = response;
|
|
} else {
|
|
cashRegisters.value = {
|
|
data: [],
|
|
total: 0
|
|
};
|
|
}
|
|
} catch (error) {
|
|
window.Notify.error('Error al cargar el historial');
|
|
cashRegisters.value = {
|
|
data: [],
|
|
total: 0
|
|
};
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const search = (query) => {
|
|
searchQuery.value = query;
|
|
loadHistory(query);
|
|
};
|
|
|
|
const goBack = () => {
|
|
router.push({ name: 'pos.cashRegister.index' });
|
|
};
|
|
|
|
const viewDetail = (cashRegister) => {
|
|
router.push({
|
|
name: 'pos.cashRegister.detail',
|
|
params: { id: cashRegister.id }
|
|
});
|
|
};
|
|
|
|
const getStatusColor = (status) => {
|
|
return status === 'closed' ? 'text-gray-600' : 'text-green-600';
|
|
};
|
|
|
|
const getStatusBadge = (status) => {
|
|
return status === 'closed'
|
|
? 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-400'
|
|
: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400';
|
|
};
|
|
|
|
const getDifferenceColor = (difference) => {
|
|
const diff = parseFloat(difference || 0);
|
|
if (Math.abs(diff) < 0.01) return 'text-gray-600 dark:text-gray-400';
|
|
return diff > 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400';
|
|
};
|
|
|
|
/** Ciclos */
|
|
onMounted(() => {
|
|
loadHistory();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<SearcherHead
|
|
title="Historial de Cajas"
|
|
placeholder="Buscar por usuario o fecha..."
|
|
@search="search"
|
|
>
|
|
<button
|
|
@click="goBack"
|
|
class="flex items-center gap-2 px-3 py-2 bg-gray-600 hover:bg-gray-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm"
|
|
>
|
|
<GoogleIcon name="arrow_back" class="text-xl" />
|
|
Volver a Caja
|
|
</button>
|
|
</SearcherHead>
|
|
|
|
<div class="pt-2 w-full">
|
|
<Table
|
|
:items="cashRegisters"
|
|
:processing="loading"
|
|
>
|
|
<template #head>
|
|
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">ID</th>
|
|
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Usuario</th>
|
|
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Apertura</th>
|
|
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cierre</th>
|
|
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Inicial</th>
|
|
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Total Ventas</th>
|
|
<th class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Diferencia</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Estado</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Acciones</th>
|
|
</template>
|
|
<template #body="{items}">
|
|
<tr
|
|
v-for="register in items"
|
|
:key="register.id"
|
|
class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
|
>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="text-sm font-mono font-semibold text-gray-900 dark:text-gray-100">
|
|
#{{ register.id }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<div>
|
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
|
{{ register.user?.name || 'N/A' }}
|
|
</p>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ register.user?.email || '' }}
|
|
</p>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<p class="text-sm text-gray-900 dark:text-gray-100">
|
|
{{ new Date(register.opened_at).toLocaleDateString('es-MX', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
}) }}
|
|
</p>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ new Date(register.opened_at).toLocaleTimeString('es-MX', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
}) }}
|
|
</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<p v-if="register.closed_at" class="text-sm text-gray-900 dark:text-gray-100">
|
|
{{ new Date(register.closed_at).toLocaleDateString('es-MX', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
}) }}
|
|
</p>
|
|
<p v-if="register.closed_at" class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ new Date(register.closed_at).toLocaleTimeString('es-MX', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
}) }}
|
|
</p>
|
|
<p v-else class="text-sm text-green-600 dark:text-green-400 font-medium">
|
|
Abierta
|
|
</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right">
|
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
|
${{ parseFloat(register.initial_cash || 0).toLocaleString('es-MX', { minimumFractionDigits: 2 }) }}
|
|
</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right">
|
|
<p class="text-sm font-bold text-blue-600 dark:text-blue-400">
|
|
${{ parseFloat(register.total_sales || 0).toLocaleString('es-MX', { minimumFractionDigits: 2 }) }}
|
|
</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right">
|
|
<p
|
|
v-if="register.status === 'closed'"
|
|
class="text-sm font-bold"
|
|
:class="getDifferenceColor(register.difference)"
|
|
>
|
|
{{ parseFloat(register.difference || 0) >= 0 ? '+' : '' }}${{ Math.abs(parseFloat(register.difference || 0)).toLocaleString('es-MX', { minimumFractionDigits: 2 }) }}
|
|
</p>
|
|
<p v-else class="text-sm text-gray-400 dark:text-gray-600">
|
|
-
|
|
</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<span
|
|
class="inline-flex px-3 py-1 text-xs font-semibold rounded-full"
|
|
:class="getStatusBadge(register.status)"
|
|
>
|
|
{{ register.status === 'closed' ? 'Cerrada' : 'Abierta' }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<button
|
|
v-if="register.status === 'closed'"
|
|
@click="viewDetail(register)"
|
|
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 transition-colors"
|
|
title="Ver detalle"
|
|
>
|
|
<GoogleIcon name="visibility" class="text-xl" />
|
|
</button>
|
|
<span v-else class="text-gray-400 dark:text-gray-600">
|
|
<GoogleIcon name="lock_open" class="text-xl" />
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
<template #empty>
|
|
<td colspan="9" class="table-cell text-center">
|
|
<div class="flex flex-col items-center justify-center py-8 text-gray-500">
|
|
<GoogleIcon
|
|
name="history"
|
|
class="text-6xl mb-2 opacity-50"
|
|
/>
|
|
<p class="font-semibold">
|
|
No hay registros de cajas
|
|
</p>
|
|
<p class="text-sm text-gray-400 mt-1">
|
|
Abre una caja para comenzar a registrar transacciones
|
|
</p>
|
|
</div>
|
|
</td>
|
|
</template>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
</template>
|