feat: repositorios, servicios, UI completa con rutas, Capacitor Android sync
This commit is contained in:
31
packages/services/src/ajustes/AjustesService.ts
Normal file
31
packages/services/src/ajustes/AjustesService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
56
packages/services/src/cuadre/CuadreService.ts
Normal file
56
packages/services/src/cuadre/CuadreService.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
16
packages/services/src/gastos/GastosService.ts
Normal file
16
packages/services/src/gastos/GastosService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
38
packages/services/src/inventario/InventarioService.ts
Normal file
38
packages/services/src/inventario/InventarioService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
56
packages/services/src/reportes/ReportesService.ts
Normal file
56
packages/services/src/reportes/ReportesService.ts
Normal 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 };
|
||||
}
|
||||
}
|
||||
37
packages/services/src/ventas/VentasService.ts
Normal file
37
packages/services/src/ventas/VentasService.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user