feat: mejorar ux y validaciones del toldo
This commit is contained in:
@@ -7,7 +7,7 @@ describe('ExportImportService', () => {
|
||||
it('exporta con versionado', () => {
|
||||
const data = {
|
||||
productores: [], productos: [], movimientosInventario: [],
|
||||
ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: []
|
||||
ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [], jornadas: []
|
||||
};
|
||||
const exp = service.exportAll(data);
|
||||
expect(exp.schemaVersion).toBe(CURRENT_SCHEMA_VERSION);
|
||||
@@ -25,14 +25,19 @@ describe('ExportImportService', () => {
|
||||
schemaVersion: 999,
|
||||
appVersion: '0.0.1',
|
||||
exportedAt: new Date().toISOString(),
|
||||
data: { productores: [], productos: [], movimientosInventario: [], ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [] }
|
||||
data: { productores: [], productos: [], movimientosInventario: [], ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [], jornadas: [] }
|
||||
};
|
||||
expect(() => service.import(bad)).toThrow('Versión de esquema incompatible');
|
||||
});
|
||||
|
||||
it('importa datos válidos v1', () => {
|
||||
const exp = service.exportAll({ productores: [], productos: [], movimientosInventario: [], ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [] });
|
||||
const exp = service.exportAll({ productores: [], productos: [], movimientosInventario: [], ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [], jornadas: [] });
|
||||
const imp = service.import(exp);
|
||||
expect(imp.schemaVersion).toBe(CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
it('migra exportaciones v1 sin jornadas', () => {
|
||||
const legacy = { schemaVersion: 1, appVersion: '0.0.1', exportedAt: new Date().toISOString(), data: { productores: [], productos: [], movimientosInventario: [], ventas: [], ventaLineas: [], gastos: [], ajustes: [], cuadres: [] } };
|
||||
expect(service.import(legacy).data.jornadas).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,6 +51,10 @@ export class CuadreService {
|
||||
if (cuadre.saldoRealEfectivo !== undefined && cuadre.saldoEsperadoEfectivo !== undefined) {
|
||||
cuadre.descuadreEfectivo = calcularDescuadreEfectivo(cuadre.saldoEsperadoEfectivo, cuadre.saldoRealEfectivo);
|
||||
}
|
||||
if (cuadre.totalQRBanco !== undefined) {
|
||||
cuadre.totalQRRegistrado = cuadre.totalVentasQR - cuadre.totalGastosQR;
|
||||
cuadre.descuadreQR = cuadre.totalQRBanco - cuadre.totalQRRegistrado;
|
||||
}
|
||||
return db.cuadres.add(cuadre);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Productor, Producto, MovimientoInventario, Venta, VentaLinea, Gasto, Ajuste, CuadreCaja } from '@toldo/core';
|
||||
import type { Productor, Producto, MovimientoInventario, Venta, VentaLinea, Gasto, Ajuste, CuadreCaja, JornadaToldo } from '@toldo/core';
|
||||
|
||||
export interface ExportData {
|
||||
schemaVersion: number;
|
||||
@@ -13,6 +13,7 @@ export interface ExportData {
|
||||
gastos: Gasto[];
|
||||
ajustes: Ajuste[];
|
||||
cuadres: CuadreCaja[];
|
||||
jornadas?: JornadaToldo[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,6 +52,6 @@ export class ExportImportService {
|
||||
throw new Error('Versión de esquema incompatible. Actualice la aplicación.');
|
||||
}
|
||||
// Simple pass-through for v1; migrations would go here
|
||||
return data as ExportData;
|
||||
return { ...(data as ExportData), data: { ...(data as ExportData).data, jornadas: (data as ExportData).data.jornadas ?? [] } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@ export class ReportesService {
|
||||
for (const productor of productores) {
|
||||
const prodsProductor = productos.filter(p => p.productorId === productor.id);
|
||||
let totalVentas = 0;
|
||||
let inventarioInicial = 0;
|
||||
let diferenciaInventario = 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));
|
||||
const movimientos = await db.movimientosInventario.where('productoId').equals(prod.id).toArray();
|
||||
inventarioInicial += movimientos.filter(m => m.tipo === 'INGRESO_INICIAL').reduce((sum, m) => sum + m.cantidad, 0);
|
||||
for (const lp of lineasProd) {
|
||||
totalVentas += lp.subtotal;
|
||||
salidas.push({ producto: prod.nombrePresentacion, cantidad: lp.cantidad, valor: lp.subtotal });
|
||||
@@ -29,6 +33,8 @@ export class ReportesService {
|
||||
|
||||
reporte.push({
|
||||
productor: productor.nombre,
|
||||
inventarioInicial,
|
||||
diferenciaInventario: (ajustes.filter(a => prodsProductor.some(p => p.id === a.productoId)) as any[]).reduce((sum, a) => sum + a.diferencia, 0),
|
||||
ajustes: ajustes.filter(a => prodsProductor.some(p => p.id === a.productoId)),
|
||||
salidas,
|
||||
totalVentas
|
||||
@@ -51,6 +57,15 @@ export class ReportesService {
|
||||
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 };
|
||||
const lineas = await db.ventaLineas.toArray();
|
||||
const ventasTotal = ventas.reduce((total, venta) => total + venta.total, 0);
|
||||
const gastosTotal = gastos.reduce((total, gasto) => total + gasto.monto, 0);
|
||||
const productores = await db.productores.toArray();
|
||||
const productos = await db.productos.toArray();
|
||||
const porProductor = productores.map((productor) => {
|
||||
const ids = productos.filter((p) => p.productorId === productor.id).map((p) => p.id);
|
||||
return { productor: productor.nombre, totalVentas: lineas.filter((l) => ids.includes(l.productoId) && ventas.some((v) => v.id === l.ventaId)).reduce((total, l) => total + l.subtotal, 0) };
|
||||
});
|
||||
return { ventas: ventas.length, gastos: gastos.length, ajustes: ajustes.length, ventasTotal, gastosTotal, porProductor, fechaInicio, fechaFin };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,16 @@ import type { Venta, VentaLinea } from '@toldo/core';
|
||||
|
||||
export class VentasService {
|
||||
async registrarVenta(venta: Venta, lineas: VentaLinea[]) {
|
||||
if (!lineas.length || lineas.some((linea) => linea.cantidad <= 0)) {
|
||||
throw new Error('La venta debe tener cantidades mayores que cero');
|
||||
}
|
||||
// Validar stock mínimo conceptual - se puede expandir
|
||||
return db.transaction('rw', db.ventas, db.ventaLineas, async () => {
|
||||
for (const linea of lineas) {
|
||||
const movimientos = await db.movimientosInventario.where('productoId').equals(linea.productoId).toArray();
|
||||
const stock = movimientos.reduce((total, movimiento) => total + movimiento.cantidad, 0);
|
||||
if (stock < linea.cantidad) throw new Error(`Stock insuficiente para ${linea.productoId}. Disponible: ${stock}`);
|
||||
}
|
||||
await db.ventas.add(venta);
|
||||
await db.ventaLineas.bulkAdd(lineas);
|
||||
// Registrar movimientos de venta
|
||||
|
||||
Reference in New Issue
Block a user