feat: UI completa funcional para todos los módulos
This commit is contained in:
1
apps/toldo-app/dist.old/assets/index-BJwB4DyA.css
Normal file
1
apps/toldo-app/dist.old/assets/index-BJwB4DyA.css
Normal file
@@ -0,0 +1 @@
|
||||
:root{--text:#6b6375;--text-h:#08060d;--bg:#fff;--border:#e5e4e7;--code-bg:#f4f3ec;--accent:#aa3bff;--accent-bg:#aa3bff1a;--accent-border:#aa3bff80;--social-bg:#f4f3ec80;--shadow:#0000001a 0 10px 15px -3px, #0000000d 0 4px 6px -2px;--sans:system-ui, "Segoe UI", Roboto, sans-serif;--heading:system-ui, "Segoe UI", Roboto, sans-serif;--mono:ui-monospace, Consolas, monospace;font:18px/145% var(--sans);letter-spacing:.18px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;color:var(--text);background:var(--bg);font-synthesis:none;text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}@media (width<=1024px){:root{font-size:16px}}@media (prefers-color-scheme:dark){:root{--text:#9ca3af;--text-h:#f3f4f6;--bg:#16171d;--border:#2e303a;--code-bg:#1f2028;--accent:#c084fc;--accent-bg:#c084fc26;--accent-border:#c084fc80;--social-bg:#2f303a80;--shadow:#0006 0 10px 15px -3px, #00000040 0 4px 6px -2px}#social .button-icon{filter:invert()brightness(2)}}body{margin:0}h1,h2{font-family:var(--heading);color:var(--text-h);font-weight:500}h1{letter-spacing:-1.68px;margin:32px 0;font-size:56px}@media (width<=1024px){h1{margin:20px 0;font-size:36px}}h2{letter-spacing:-.24px;margin:0 0 8px;font-size:24px;line-height:118%}@media (width<=1024px){h2{font-size:20px}}p{margin:0}code,.counter{font-family:var(--mono);color:var(--text-h);border-radius:4px;display:inline-flex}code{background:var(--code-bg);padding:4px 8px;font-size:15px;line-height:135%}.counter{color:var(--accent);background:var(--accent-bg);border:2px solid #0000;border-radius:5px;margin-bottom:24px;padding:5px 10px;font-size:16px;transition:border-color .3s}.counter:hover{border-color:var(--accent-border)}.counter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.hero{position:relative}.hero .base,.hero .framework,.hero .vite{margin:0 auto;inset-inline:0}.hero .base{z-index:0;width:170px;position:relative}.hero .framework,.hero .vite{position:absolute}.hero .framework{z-index:1;height:28px;top:34px;transform:perspective(2000px)rotate(300deg)rotateX(44deg)rotateY(39deg)scale(1.4)}.hero .vite{z-index:0;width:auto;height:26px;top:107px;transform:perspective(2000px)rotate(300deg)rotateX(40deg)rotateY(39deg)scale(.8)}#app{text-align:center;border-inline:1px solid var(--border);box-sizing:border-box;flex-direction:column;width:1126px;max-width:100%;min-height:100svh;margin:0 auto;display:flex}#center{flex-direction:column;flex-grow:1;place-content:center;place-items:center;gap:25px;display:flex}@media (width<=1024px){#center{gap:18px;padding:32px 20px 24px}}#next-steps{border-top:1px solid var(--border);text-align:left;display:flex}#next-steps>div{flex:1 1 0;padding:32px}@media (width<=1024px){#next-steps>div{padding:24px 20px}}#next-steps .icon{width:22px;height:22px;margin-bottom:16px}@media (width<=1024px){#next-steps{text-align:center;flex-direction:column}}#docs{border-right:1px solid var(--border)}@media (width<=1024px){#docs{border-right:none;border-bottom:1px solid var(--border)}}#next-steps ul{gap:8px;margin:32px 0 0;padding:0;list-style:none;display:flex}#next-steps ul .logo{height:18px}#next-steps ul a{color:var(--text-h);background:var(--social-bg);border-radius:6px;align-items:center;gap:8px;padding:6px 12px;font-size:16px;text-decoration:none;transition:box-shadow .3s;display:flex}#next-steps ul a:hover{box-shadow:var(--shadow)}#next-steps ul a .button-icon{width:18px;height:18px}@media (width<=1024px){#next-steps ul{flex-wrap:wrap;justify-content:center;margin-top:20px}#next-steps ul li{flex:calc(50% - 8px)}#next-steps ul a{box-sizing:border-box;justify-content:center;width:100%}}#spacer{border-top:1px solid var(--border);height:88px}@media (width<=1024px){#spacer{height:48px}}.ticks{width:100%;position:relative}.ticks:before,.ticks:after{content:"";border:5px solid #0000;position:absolute;top:-4.5px}.ticks:before{border-left-color:var(--border);left:0}.ticks:after{border-right-color:var(--border);right:0}.layout[data-v-c31748c9]{font-family:system-ui,sans-serif}header[data-v-c31748c9]{color:#fff;background:#2d5f3b;padding:1rem}nav[data-v-c31748c9]{flex-wrap:wrap;gap:1rem;margin-top:.5rem;display:flex}nav a[data-v-c31748c9]{color:#fff;text-decoration:none}nav a.router-link-active[data-v-c31748c9]{text-decoration:underline}main[data-v-c31748c9]{padding:1rem}
|
||||
1
apps/toldo-app/dist.old/assets/index-C8YPrbL8.js
Normal file
1
apps/toldo-app/dist.old/assets/index-C8YPrbL8.js
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 496 B |
@@ -1,95 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import heroImg from '../assets/hero.png'
|
||||
import viteLogo from '../assets/vite.svg'
|
||||
import vueLogo from '../assets/vue.svg'
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="center">
|
||||
<div class="hero">
|
||||
<img :src="heroImg" class="base" width="170" height="179" alt="" />
|
||||
<img :src="vueLogo" class="framework" alt="Vue logo" />
|
||||
<img :src="viteLogo" class="vite" alt="Vite logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h1>Get started</h1>
|
||||
<p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
|
||||
</div>
|
||||
<button type="button" class="counter" @click="count++">
|
||||
Count is {{ count }}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<div class="ticks"></div>
|
||||
|
||||
<section id="next-steps">
|
||||
<div id="docs">
|
||||
<svg class="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#documentation-icon"></use>
|
||||
</svg>
|
||||
<h2>Documentation</h2>
|
||||
<p>Your questions, answered</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vite.dev/" target="_blank">
|
||||
<img class="logo" :src="viteLogo" alt="" />
|
||||
Explore Vite
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://vuejs.org/" target="_blank">
|
||||
<img class="button-icon" :src="vueLogo" alt="" />
|
||||
Learn more
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="social">
|
||||
<svg class="icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#social-icon"></use>
|
||||
</svg>
|
||||
<h2>Connect with us</h2>
|
||||
<p>Join the Vite community</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/vitejs/vite" target="_blank">
|
||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#github-icon"></use>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://chat.vite.dev/" target="_blank">
|
||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#discord-icon"></use>
|
||||
</svg>
|
||||
Discord
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://x.com/vite_js" target="_blank">
|
||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#x-icon"></use>
|
||||
</svg>
|
||||
X.com
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://bsky.app/profile/vite.dev" target="_blank">
|
||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
||||
<use href="/icons.svg#bluesky-icon"></use>
|
||||
</svg>
|
||||
Bluesky
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="ticks"></div>
|
||||
<section id="spacer"></section>
|
||||
</template>
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"types": ["vite/client"],
|
||||
"allowArbitraryExtensions": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"paths": {
|
||||
"@toldo/core": ["../../packages/core/src"],
|
||||
"@toldo/data": ["../../packages/data/src"],
|
||||
"@toldo/services": ["../../packages/services/src"],
|
||||
"@toldo/ui": ["../../packages/ui/src"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user