feat: repositorios, servicios, UI completa con rutas, Capacitor Android sync

This commit is contained in:
mono
2026-10-24 21:11:50 -05:00
parent 0c7cf651ce
commit df3838c2f3
36 changed files with 4224 additions and 402 deletions

View File

@@ -14,10 +14,10 @@
"build": "tsc"
},
"dependencies": {
"@toldo/core": "workspace:*",
"@toldo/core": "file:../core",
"dexie": "^4.0.10"
},
"devDependencies": {
"typescript": "~6.0.3"
}
}
}

View File

@@ -1 +1,2 @@
export * from './db/schema';
export * from './repositories';

View File

@@ -0,0 +1,22 @@
import { db } from '../db/schema';
import type { Ajuste } from '@toldo/core';
import { validateAjusteMotivo } from '@toldo/core';
export class AjusteRepository {
async create(ajuste: Ajuste): Promise<string> {
validateAjusteMotivo(ajuste.motivo);
return db.ajustes.add(ajuste);
}
async getAll(): Promise<Ajuste[]> {
return db.ajustes.toArray();
}
async getByFecha(fecha: Date): Promise<Ajuste[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.ajustes.where('fecha').between(start, end).toArray();
}
}

View File

@@ -0,0 +1,20 @@
import { db } from '../db/schema';
import type { CuadreCaja } from '@toldo/core';
export class CuadreRepository {
async create(cuadre: CuadreCaja): Promise<string> {
return db.cuadres.add(cuadre);
}
async getByFecha(fecha: Date): Promise<CuadreCaja[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.cuadres.where('fecha').between(start, end).toArray();
}
async getAll(): Promise<CuadreCaja[]> {
return db.cuadres.toArray();
}
}

View File

@@ -0,0 +1,20 @@
import { db } from '../db/schema';
import type { Gasto } from '@toldo/core';
export class GastoRepository {
async create(gasto: Gasto): Promise<string> {
return db.gastos.add(gasto);
}
async getByFecha(fecha: Date): Promise<Gasto[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.gastos.where('fecha').between(start, end).toArray();
}
async getAll(): Promise<Gasto[]> {
return db.gastos.toArray();
}
}

View File

@@ -0,0 +1,16 @@
import { db } from '../db/schema';
import type { MovimientoInventario } from '@toldo/core';
export class MovimientoInventarioRepository {
async create(mov: MovimientoInventario): Promise<string> {
return db.movimientosInventario.add(mov);
}
async getByProducto(productoId: string): Promise<MovimientoInventario[]> {
return db.movimientosInventario.where('productoId').equals(productoId).toArray();
}
async getAll(): Promise<MovimientoInventario[]> {
return db.movimientosInventario.toArray();
}
}

View File

@@ -0,0 +1,30 @@
import { db } from '../db/schema';
import type { Producto } from '@toldo/core';
export class ProductoRepository {
async getAll(): Promise<Producto[]> {
return db.productos.toArray();
}
async getByProductor(productorId: string): Promise<Producto[]> {
return db.productos.where('productorId').equals(productorId).toArray();
}
async getById(id: string): Promise<Producto | undefined> {
return db.productos.get(id);
}
async create(producto: Producto): Promise<string> {
// Ensure uniqueness by (productorId, nombrePresentacion)
const exists = await db.productos
.where('[productorId+nombrePresentacion]')
.equals([producto.productorId, producto.nombrePresentacion])
.first();
if (exists) throw new Error('Ya existe un producto con ese nombre para este productor');
return db.productos.add(producto);
}
async update(producto: Producto): Promise<number> {
return db.productos.put(producto);
}
}

View File

@@ -0,0 +1,24 @@
import { db } from '../db/schema';
import type { Productor } from '@toldo/core';
export class ProductorRepository {
async getAll(): Promise<Productor[]> {
return db.productores.toArray();
}
async getById(id: string): Promise<Productor | undefined> {
return db.productores.get(id);
}
async create(productor: Productor): Promise<string> {
return db.productores.add(productor);
}
async update(productor: Productor): Promise<number> {
return db.productores.put(productor);
}
async delete(id: string): Promise<void> {
return db.productores.delete(id);
}
}

View File

@@ -0,0 +1,24 @@
import { db } from '../db/schema';
import type { Venta, VentaLinea } from '@toldo/core';
export class VentaRepository {
async create(venta: Venta, lineas: VentaLinea[]): Promise<string> {
return db.transaction('rw', db.ventas, db.ventaLineas, async () => {
await db.ventas.add(venta);
await db.ventaLineas.bulkAdd(lineas);
return venta.id;
});
}
async getByFecha(fecha: Date): Promise<Venta[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.ventas.where('fecha').between(start, end).toArray();
}
async getAll(): Promise<Venta[]> {
return db.ventas.toArray();
}
}

View File

@@ -0,0 +1,7 @@
export * from './ProductorRepository';
export * from './ProductoRepository';
export * from './VentaRepository';
export * from './GastoRepository';
export * from './AjusteRepository';
export * from './MovimientoInventarioRepository';
export * from './CuadreRepository';

View File

@@ -15,8 +15,8 @@
"test": "vitest"
},
"dependencies": {
"@toldo/core": "workspace:*",
"@toldo/data": "workspace:*"
"@toldo/core": "file:../core",
"@toldo/data": "file:../data"
},
"devDependencies": {
"typescript": "~6.0.3",

View File

@@ -0,0 +1,31 @@
import { db } from '@toldo/data';
import type { Ajuste, MovimientoInventario } from '@toldo/core';
import { validateAjusteMotivo } from '@toldo/core';
export class AjustesService {
async crearAjuste(ajuste: Ajuste) {
validateAjusteMotivo(ajuste.motivo);
return db.transaction('rw', db.ajustes, db.movimientosInventario, async () => {
await db.ajustes.add(ajuste);
await db.movimientosInventario.add({
id: crypto.randomUUID(),
fecha: ajuste.fecha,
tipo: 'AJUSTE',
productoId: ajuste.productoId,
cantidad: ajuste.diferencia,
motivo: ajuste.motivo,
referenciaId: ajuste.id,
createdAt: new Date()
});
return ajuste.id;
});
}
async getAjustesDelDia(fecha: Date): Promise<Ajuste[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.ajustes.where('fecha').between(start, end).toArray();
}
}

View File

@@ -0,0 +1,56 @@
import { db } from '@toldo/data';
import type { CuadreCaja } from '@toldo/core';
import { calcularSaldoEsperadoEfectivo, calcularDescuadreEfectivo, calcularTotalVentas } from '@toldo/core';
export class CuadreService {
async calcularCuadreDelDia(fecha: Date, baseInicial: number): Promise<Partial<CuadreCaja>> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
const ventas = await db.ventas.where('fecha').between(start, end).toArray();
const lineas = await db.ventaLineas.toArray();
const gastos = await db.gastos.where('fecha').between(start, end).toArray();
let totalVentasEfectivo = 0;
let totalVentasQR = 0;
for (const venta of ventas) {
const vLineas = lineas.filter(l => l.ventaId === venta.id);
for (const l of vLineas) {
if (l.formaPago === 'EFECTIVO') totalVentasEfectivo += l.subtotal;
else totalVentasQR += l.subtotal;
}
}
let totalGastosEfectivo = 0;
let totalGastosQR = 0;
for (const g of gastos) {
if (g.formaPago === 'EFECTIVO') totalGastosEfectivo += g.monto;
else if (g.formaPago === 'QR') totalGastosQR += g.monto;
}
const saldoEsperadoEfectivo = calcularSaldoEsperadoEfectivo(baseInicial, totalVentasEfectivo, totalGastosEfectivo);
const totalVentas = calcularTotalVentas(totalVentasEfectivo, totalVentasQR);
return {
fecha,
baseInicial,
totalVentasEfectivo,
totalVentasQR,
totalGastosEfectivo,
totalGastosQR,
saldoEsperadoEfectivo,
totalVentas,
createdAt: new Date()
};
}
async guardarCuadre(cuadre: CuadreCaja) {
if (cuadre.saldoRealEfectivo !== undefined && cuadre.saldoEsperadoEfectivo !== undefined) {
cuadre.descuadreEfectivo = calcularDescuadreEfectivo(cuadre.saldoEsperadoEfectivo, cuadre.saldoRealEfectivo);
}
return db.cuadres.add(cuadre);
}
}

View File

@@ -0,0 +1,16 @@
import { db } from '@toldo/data';
import type { Gasto } from '@toldo/core';
export class GastosService {
async registrarGasto(gasto: Gasto) {
return db.gastos.add(gasto);
}
async getGastosDelDia(fecha: Date): Promise<Gasto[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.gastos.where('fecha').between(start, end).toArray();
}
}

View File

@@ -1 +1,7 @@
export * from './ventas/VentasService';
export * from './gastos/GastosService';
export * from './ajustes/AjustesService';
export * from './inventario/InventarioService';
export * from './cuadre/CuadreService';
export * from './reportes/ReportesService';
export * from './export-import/ExportImportService';

View File

@@ -0,0 +1,38 @@
import { db } from '@toldo/data';
import type { MovimientoInventario } from '@toldo/core';
export class InventarioService {
async registrarIngresoInicial(productoId: string, cantidad: number, fecha: Date) {
const mov: MovimientoInventario = {
id: crypto.randomUUID(),
fecha,
tipo: 'INGRESO_INICIAL',
productoId,
cantidad,
createdAt: new Date()
};
return db.movimientosInventario.add(mov.id, mov);
}
async registrarIngresoSemana(productoId: string, cantidad: number, fecha: Date, recibidoPor: string) {
const mov: MovimientoInventario = {
id: crypto.randomUUID(),
fecha,
tipo: 'INGRESO_SEMANA',
productoId,
cantidad,
recibidoPor,
createdAt: new Date()
};
return db.movimientosInventario.add(mov.id, mov);
}
async calcularSaldoProducto(productoId: string): Promise<number> {
const movs = await db.movimientosInventario.where('productoId').equals(productoId).toArray();
return movs.reduce((acc, m) => acc + m.cantidad, 0);
}
async getMovimientosPorProducto(productoId: string): Promise<MovimientoInventario[]> {
return db.movimientosInventario.where('productoId').equals(productoId).toArray();
}
}

View File

@@ -0,0 +1,56 @@
import { db } from '@toldo/data';
export class ReportesService {
async reportePorProductor(fecha: Date) {
const productores = await db.productores.toArray();
const productos = await db.productos.toArray();
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
const ventas = await db.ventas.where('fecha').between(start, end).toArray();
const lineas = await db.ventaLineas.toArray();
const ajustes = await db.ajustes.where('fecha').between(start, end).toArray();
const reporte: any[] = [];
for (const productor of productores) {
const prodsProductor = productos.filter(p => p.productorId === productor.id);
let totalVentas = 0;
const salidas: any[] = [];
for (const prod of prodsProductor) {
const lineasProd = lineas.filter(l => l.productoId === prod.id && ventas.some(v => v.id === l.ventaId));
for (const lp of lineasProd) {
totalVentas += lp.subtotal;
salidas.push({ producto: prod.nombrePresentacion, cantidad: lp.cantidad, valor: lp.subtotal });
}
}
reporte.push({
productor: productor.nombre,
ajustes: ajustes.filter(a => prodsProductor.some(p => p.id === a.productoId)),
salidas,
totalVentas
});
}
return reporte;
}
async reporteCuadre(fecha: Date) {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
const cuadres = await db.cuadres.where('fecha').between(start, end).toArray();
return cuadres;
}
async consolidadoPorRango(fechaInicio: Date, fechaFin: Date) {
const ventas = await db.ventas.where('fecha').between(fechaInicio, fechaFin).toArray();
const gastos = await db.gastos.where('fecha').between(fechaInicio, fechaFin).toArray();
const ajustes = await db.ajustes.where('fecha').between(fechaInicio, fechaFin).toArray();
return { ventas: ventas.length, gastos: gastos.length, ajustes: ajustes.length };
}
}

View File

@@ -0,0 +1,37 @@
import { db } from '@toldo/data';
import type { Venta, VentaLinea } from '@toldo/core';
export class VentasService {
async registrarVenta(venta: Venta, lineas: VentaLinea[]) {
// Validar stock mínimo conceptual - se puede expandir
return db.transaction('rw', db.ventas, db.ventaLineas, async () => {
await db.ventas.add(venta);
await db.ventaLineas.bulkAdd(lineas);
// Registrar movimientos de venta
for (const linea of lineas) {
await db.movimientosInventario.add({
id: crypto.randomUUID(),
fecha: venta.fecha,
tipo: 'VENTA',
productoId: linea.productoId,
cantidad: -Math.abs(linea.cantidad),
referenciaId: venta.id,
createdAt: new Date()
});
}
return venta.id;
});
}
async getVentasDelDia(fecha: Date): Promise<Venta[]> {
const start = new Date(fecha);
start.setHours(0, 0, 0, 0);
const end = new Date(fecha);
end.setHours(23, 59, 59, 999);
return db.ventas.where('fecha').between(start, end).toArray();
}
async getLineasPorVenta(ventaId: string): Promise<VentaLinea[]> {
return db.ventaLineas.where('ventaId').equals(ventaId).toArray();
}
}

View File

@@ -14,10 +14,10 @@
"build": "tsc"
},
"dependencies": {
"@toldo/core": "workspace:*",
"@toldo/services": "workspace:*"
"@toldo/core": "file:../core",
"@toldo/services": "file:../services"
},
"devDependencies": {
"typescript": "~6.0.3"
}
}
}