feat: UI completa funcional para todos los módulos
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user