feat: UI completa funcional para todos los módulos

This commit is contained in:
mono
2026-10-24 22:57:04 -05:00
parent c81ef268ff
commit 27b084dd52
21 changed files with 461 additions and 121 deletions

View File

@@ -1,6 +1,68 @@
<template>
<div>
<h2>Ajustes</h2>
<p>Módulo en desarrollo</p>
<p><strong>Requiere motivo obligatorio</strong></p>
<section>
<select v-model="productoSel">
<option value="">Seleccionar producto</option>
<option v-for="prod in productos" :key="prod.id" :value="prod.id">{{ prod.nombrePresentacion }}</option>
</select>
<input v-model.number="cantidadNueva" type="number" placeholder="Cantidad nueva" />
<input v-model="motivo" placeholder="Motivo del ajuste (OBLIGATORIO)" />
<button @click="ajustar">Realizar Ajuste</button>
</section>
<section>
<h3>Ajustes del día</h3>
<ul>
<li v-for="a in ajustes" :key="a.id">{{ a.productoId }}: {{ a.cantidadAnterior }} → {{ a.cantidadNueva }} | Motivo: {{ a.motivo }}</li>
</ul>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { db } from '@toldo/data';
import { AjustesService, InventarioService } from '@toldo/services';
import { validateAjusteMotivo } from '@toldo/core';
const ajustesService = new AjustesService();
const invService = new InventarioService();
const productos = ref<any[]>([]);
const productoSel = ref('');
const cantidadNueva = ref(0);
const motivo = ref('');
const ajustes = ref<any[]>([]);
async function cargar() {
productos.value = await db.productos.toArray();
ajustes.value = await ajustesService.getAjustesDelDia(new Date());
}
async function ajustar() {
try {
validateAjusteMotivo(motivo.value);
if (!productoSel.value) return;
const saldoActual = await invService.calcularSaldoProducto(productoSel.value);
const diferencia = cantidadNueva.value - saldoActual;
await ajustesService.crearAjuste({
id: crypto.randomUUID(),
fecha: new Date(),
productoId: productoSel.value,
cantidadAnterior: saldoActual,
cantidadNueva: cantidadNueva.value,
diferencia,
motivo: motivo.value,
createdAt: new Date()
});
motivo.value = '';
cantidadNueva.value = 0;
await cargar();
alert('Ajuste registrado con motivo');
} catch (e: any) {
alert(e.message);
}
}
onMounted(cargar);
</script>

View File

@@ -1,6 +1,46 @@
<template>
<div>
<h2>Cuadre</h2>
<p>Módulo en desarrollo</p>
<h2>Cuadre de Caja</h2>
<section>
<label>Base Inicial: </label>
<input v-model.number="baseInicial" type="number" />
<button @click="calcular">Calcular Cuadre</button>
</section>
<section v-if="cuadre">
<h3>Resultado</h3>
<p>Ventas Efectivo: ${{ cuadre.totalVentasEfectivo }}</p>
<p>Ventas QR: ${{ cuadre.totalVentasQR }}</p>
<p>Gastos Efectivo: ${{ cuadre.totalGastosEfectivo }}</p>
<p>Gastos QR: ${{ cuadre.totalGastosQR }}</p>
<p><strong>Saldo Esperado Efectivo: ${{ cuadre.saldoEsperadoEfectivo }}</strong></p>
<p>Total Ventas: ${{ cuadre.totalVentas }}</p>
<label>Saldo Real en Efectivo: </label>
<input v-model.number="saldoReal" type="number" />
<button @click="guardar">Guardar Cuadre</button>
<p v-if="descuadre !== null">Descuadre: ${{ descuadre }}</p>
</section>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { CuadreService } from '@toldo/services';
const cuadreService = new CuadreService();
const baseInicial = ref(0);
const cuadre = ref<any>(null);
const saldoReal = ref(0);
const descuadre = ref<number | null>(null);
async function calcular() {
cuadre.value = await cuadreService.calcularCuadreDelDia(new Date(), baseInicial.value);
}
async function guardar() {
if (!cuadre.value) return;
cuadre.value.saldoRealEfectivo = saldoReal.value;
await cuadreService.guardarCuadre({ ...cuadre.value, id: crypto.randomUUID(), createdAt: new Date() });
descuadre.value = saldoReal.value - cuadre.value.saldoEsperadoEfectivo;
alert('Cuadre guardado');
}
</script>

View File

@@ -1,6 +1,70 @@
<template>
<div>
<h2>Exportar</h2>
<p>Módulo en desarrollo</p>
<h2>Exportar / Importar</h2>
<section>
<h3>Exportar Datos</h3>
<button @click="exportar">Exportar JSON</button>
</section>
<section>
<h3>Importar Datos</h3>
<input type="file" accept=".json" @change="onFileChange" />
<button @click="importar" :disabled="!archivo">Importar JSON</button>
</section>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { db } from '@toldo/data';
import { ExportImportService } from '@toldo/services';
const exportService = new ExportImportService();
const archivo = ref<File | null>(null);
function onFileChange(e: any) {
archivo.value = e.target.files[0];
}
async function exportar() {
const data = {
productores: await db.productores.toArray(),
productos: await db.productos.toArray(),
movimientosInventario: await db.movimientosInventario.toArray(),
ventas: await db.ventas.toArray(),
ventaLineas: await db.ventaLineas.toArray(),
gastos: await db.gastos.toArray(),
ajustes: await db.ajustes.toArray(),
cuadres: await db.cuadres.toArray(),
};
const exp = exportService.exportAll(data);
const blob = new Blob([JSON.stringify(exp, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `toldo-export-${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
}
async function importar() {
if (!archivo.value) return;
const text = await archivo.value.text();
try {
const json = JSON.parse(text);
const imp = exportService.import(json);
if (imp.data.productores.length) await db.productores.bulkPut(imp.data.productores);
if (imp.data.productos.length) await db.productos.bulkPut(imp.data.productos);
if (imp.data.movimientosInventario.length) await db.movimientosInventario.bulkPut(imp.data.movimientosInventario);
if (imp.data.ventas.length) await db.ventas.bulkPut(imp.data.ventas);
if (imp.data.ventaLineas.length) await db.ventaLineas.bulkPut(imp.data.ventaLineas);
if (imp.data.gastos.length) await db.gastos.bulkPut(imp.data.gastos);
if (imp.data.ajustes.length) await db.ajustes.bulkPut(imp.data.ajustes);
if (imp.data.cuadres.length) await db.cuadres.bulkPut(imp.data.cuadres);
alert('Datos importados correctamente');
} catch (e: any) {
alert(e.message);
}
}
</script>

View File

@@ -1,6 +1,53 @@
<template>
<div>
<h2>Gastos</h2>
<p>Módulo en desarrollo</p>
<section>
<input v-model="concepto" placeholder="Concepto (cinta, lapicero, bolsas...)" />
<input v-model.number="monto" type="number" placeholder="Monto" />
<select v-model="formaPago">
<option value="EFECTIVO">Efectivo</option>
<option value="QR">QR</option>
</select>
<button @click="registrar">Registrar Gasto</button>
</section>
<section>
<h3>Gastos del día</h3>
<ul>
<li v-for="g in gastos" :key="g.id">{{ g.concepto }} - ${{ g.monto }} ({{ g.formaPago }})</li>
</ul>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { GastosService } from '@toldo/services';
const gastosService = new GastosService();
const concepto = ref('');
const monto = ref(0);
const formaPago = ref<'EFECTIVO' | 'QR'>('EFECTIVO');
const gastos = ref<any[]>([]);
async function cargar() {
gastos.value = await gastosService.getGastosDelDia(new Date());
}
async function registrar() {
if (!concepto.value || monto.value <= 0) return;
await gastosService.registrarGasto({
id: crypto.randomUUID(),
fecha: new Date(),
concepto: concepto.value,
monto: monto.value,
formaPago: formaPago.value,
createdAt: new Date()
} as any);
concepto.value = '';
monto.value = 0;
await cargar();
alert('Gasto registrado');
}
onMounted(cargar);
</script>

View File

@@ -1,6 +1,105 @@
<template>
<div>
<h2>Inventario</h2>
<p>Módulo en desarrollo</p>
<section>
<h3>Productores</h3>
<input v-model="nuevoProductor" placeholder="Nombre del productor" />
<button @click="crearProductor">Agregar Productor</button>
<ul>
<li v-for="p in productores" :key="p.id">{{ p.nombre }}</li>
</ul>
</section>
<section>
<h3>Productos (por productor)</h3>
<select v-model="productorSel">
<option value="">Seleccionar productor</option>
<option v-for="p in productores" :key="p.id" :value="p.id">{{ p.nombre }}</option>
</select>
<input v-model="nombreProd" placeholder="Ej: Café molido 250gr" />
<input v-model.number="precioProd" type="number" placeholder="Precio unitario" />
<button @click="crearProducto">Agregar Producto</button>
<ul>
<li v-for="prod in productos" :key="prod.id">{{ prod.nombrePresentacion }} - ${{ prod.precioUnitario }}</li>
</ul>
</section>
<section>
<h3>Ingreso Inicial</h3>
<select v-model="prodInv">
<option value="">Seleccionar producto</option>
<option v-for="prod in productos" :key="prod.id" :value="prod.id">{{ prod.nombrePresentacion }}</option>
</select>
<input v-model.number="cantInv" type="number" placeholder="Cantidad" />
<button @click="ingresoInicial">Registrar Ingreso Inicial</button>
</section>
<section>
<h3>Ingreso entre semana</h3>
<select v-model="prodInv2">
<option value="">Seleccionar producto</option>
<option v-for="prod in productos" :key="prod.id" :value="prod.id">{{ prod.nombrePresentacion }}</option>
</select>
<input v-model.number="cantInv2" type="number" placeholder="Cantidad" />
<input v-model="recibidoPor" placeholder="Recibido por" />
<button @click="ingresoSemana">Registrar Ingreso</button>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { db } from '@toldo/data';
import { InventarioService } from '@toldo/services';
const invService = new InventarioService();
const productores = ref<any[]>([]);
const productos = ref<any[]>([]);
const nuevoProductor = ref('');
const productorSel = ref('');
const nombreProd = ref('');
const precioProd = ref(0);
const prodInv = ref('');
const cantInv = ref(0);
const prodInv2 = ref('');
const cantInv2 = ref(0);
const recibidoPor = ref('');
async function cargar() {
productores.value = await db.productores.toArray();
productos.value = await db.productos.toArray();
}
async function crearProductor() {
if (!nuevoProductor.value) return;
await db.productores.add({ id: crypto.randomUUID(), nombre: nuevoProductor.value, activo: true, createdAt: new Date() });
nuevoProductor.value = '';
await cargar();
}
async function crearProducto() {
if (!productorSel.value || !nombreProd.value) return;
await db.productos.add({ id: crypto.randomUUID(), productorId: productorSel.value, nombrePresentacion: nombreProd.value, precioUnitario: precioProd.value, activo: true, createdAt: new Date() });
nombreProd.value = '';
precioProd.value = 0;
await cargar();
}
async function ingresoInicial() {
if (!prodInv.value || cantInv.value <= 0) return;
await invService.registrarIngresoInicial(prodInv.value, cantInv.value, new Date());
cantInv.value = 0;
alert('Ingreso inicial registrado');
}
async function ingresoSemana() {
if (!prodInv2.value || cantInv2.value <= 0 || !recibidoPor.value) return;
await invService.registrarIngresoSemana(prodInv2.value, cantInv2.value, new Date(), recibidoPor.value);
cantInv2.value = 0;
recibidoPor.value = '';
alert('Ingreso entre semana registrado');
}
onMounted(cargar);
</script>

View File

@@ -1,6 +1,38 @@
<template>
<div>
<h2>Reportes</h2>
<p>Módulo en desarrollo</p>
<button @click="generarPorProductor">Reporte por Productor</button>
<button @click="generarCuadre">Reporte de Cuadre</button>
<section v-if="reporteProd.length">
<h3>Por Productor</h3>
<div v-for="r in reporteProd" :key="r.productor">
<h4>{{ r.productor }}</h4>
<p>Ventas: ${{ r.totalVentas }}</p>
<p>Ajustes: {{ r.ajustes.length }}</p>
</div>
</section>
<section v-if="reporteCua.length">
<h3>Cuadre</h3>
<div v-for="c in reporteCua" :key="c.id">
<p>Base: ${{ c.baseInicial }} | Ventas: ${{ c.totalVentas }} | Esperado Efectivo: ${{ c.saldoEsperadoEfectivo }} | Descuadre: ${{ c.descuadreEfectivo }}</p>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ReportesService } from '@toldo/services';
const reportesService = new ReportesService();
const reporteProd = ref<any[]>([]);
const reporteCua = ref<any[]>([]);
async function generarPorProductor() {
reporteProd.value = await reportesService.reportePorProductor(new Date());
}
async function generarCuadre() {
reporteCua.value = await reportesService.reporteCuadre(new Date());
}
</script>

View File

@@ -1,6 +1,95 @@
<template>
<div>
<h2>Ventas</h2>
<p>Módulo en desarrollo</p>
<section>
<h3>Nueva Venta</h3>
<select v-model="productoSel">
<option value="">Seleccionar producto</option>
<option v-for="prod in productos" :key="prod.id" :value="prod.id">{{ prod.nombrePresentacion }} - ${{ prod.precioUnitario }}</option>
</select>
<input v-model.number="cantidad" type="number" placeholder="Cantidad" />
<select v-model="formaPago">
<option value="EFECTIVO">Efectivo</option>
<option value="QR">QR</option>
</select>
<button @click="agregarLinea">Agregar Línea</button>
</section>
<section>
<h3>Carrito</h3>
<table v-if="lineas.length">
<thead><tr><th>Producto</th><th>Cant</th><th>Precio</th><th>Subtotal</th><th>Forma</th></tr></thead>
<tbody>
<tr v-for="(l, i) in lineas" :key="i">
<td>{{ getProdNombre(l.productoId) }}</td>
<td>{{ l.cantidad }}</td>
<td>${{ l.precioUnitario }}</td>
<td>${{ l.subtotal }}</td>
<td>{{ l.formaPago }}</td>
</tr>
</tbody>
</table>
<p v-if="lineas.length"><strong>Total: ${{ total }}</strong></p>
<button v-if="lineas.length" @click="registrarVenta">Registrar Venta</button>
</section>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { db } from '@toldo/data';
import { VentasService } from '@toldo/services';
const ventasService = new VentasService();
const productos = ref<any[]>([]);
const productoSel = ref('');
const cantidad = ref(1);
const formaPago = ref('EFECTIVO');
const lineas = ref<any[]>([]);
async function cargar() {
productos.value = await db.productos.toArray();
}
function getProdNombre(id: string) {
return productos.value.find(p => p.id === id)?.nombrePresentacion || id;
}
function agregarLinea() {
if (!productoSel.value || cantidad.value <= 0) return;
const prod = productos.value.find(p => p.id === productoSel.value);
if (!prod) return;
lineas.value.push({
id: crypto.randomUUID(),
ventaId: '',
productoId: prod.id,
cantidad: cantidad.value,
precioUnitario: prod.precioUnitario,
subtotal: prod.precioUnitario * cantidad.value,
formaPago: formaPago.value
});
productoSel.value = '';
cantidad.value = 1;
}
const total = computed(() => lineas.value.reduce((acc, l) => acc + l.subtotal, 0));
async function registrarVenta() {
const ventaId = crypto.randomUUID();
const venta = {
id: ventaId,
fecha: new Date(),
lineas: lineas.value.map(l => ({ ...l, ventaId })),
subtotal: total.value,
total: total.value,
createdAt: new Date()
};
const lineasVenta = lineas.value.map(l => ({ ...l, ventaId }));
await ventasService.registrarVenta(venta, lineasVenta);
lineas.value = [];
alert('Venta registrada');
}
onMounted(cargar);
</script>