feat: implementar jornadas y validar aliases
This commit is contained in:
105
packages/services/src/JornadaService.ts
Normal file
105
packages/services/src/JornadaService.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { db } from '@toldo/data';
|
||||
import type { JornadaToldo, Venta, VentaLinea, Gasto, CuadreCaja } from '@toldo/core';
|
||||
import { validarApertura, puedeAbrirJornada } from '@toldo/core';
|
||||
import { VentasService } from './ventas/VentasService';
|
||||
import { GastosService } from './gastos/GastosService';
|
||||
import { CuadreService } from './cuadre/CuadreService';
|
||||
|
||||
export class JornadaService {
|
||||
private ventasService = new VentasService();
|
||||
private gastosService = new GastosService();
|
||||
private cuadreService = new CuadreService();
|
||||
|
||||
async getJornadaAbierta(): Promise<JornadaToldo | undefined> {
|
||||
return db.jornadas.where('estado').equals('ABIERTA').first();
|
||||
}
|
||||
|
||||
async abrirJornada(responsables: string[], baseInicial: number): Promise<string> {
|
||||
const err = validarApertura(responsables, baseInicial);
|
||||
if (err) throw new Error(err);
|
||||
const abierta = await this.getJornadaAbierta();
|
||||
if (!puedeAbrirJornada(abierta ? 1 : 0)) throw new Error('Ya existe una jornada abierta');
|
||||
const jornada: JornadaToldo = {
|
||||
id: crypto.randomUUID(),
|
||||
fecha: new Date(),
|
||||
estado: 'ABIERTA',
|
||||
responsables,
|
||||
baseInicial,
|
||||
fechaApertura: new Date(),
|
||||
ventaIds: [],
|
||||
gastoIds: [],
|
||||
ajusteIds: [],
|
||||
createdAt: new Date()
|
||||
};
|
||||
return db.jornadas.add(jornada as any);
|
||||
}
|
||||
|
||||
async registrarVentaEnJornada(jornadaId: string, venta: Venta, lineas: VentaLinea[]): Promise<string> {
|
||||
const jornada: any = await db.jornadas.get(jornadaId);
|
||||
if (!jornada) throw new Error('Jornada no encontrada');
|
||||
if (jornada.estado !== 'ABIERTA') throw new Error('No se pueden registrar ventas en jornada cerrada');
|
||||
const ventaId = await this.ventasService.registrarVenta({ ...venta, id: venta.id }, lineas);
|
||||
await db.jornadas.put({
|
||||
...jornada,
|
||||
ventaIds: [...(jornada.ventaIds || []), venta.id],
|
||||
updatedAt: new Date()
|
||||
});
|
||||
return ventaId;
|
||||
}
|
||||
|
||||
async registrarGastoEnJornada(jornadaId: string, gasto: Gasto): Promise<string> {
|
||||
const jornada: any = await db.jornadas.get(jornadaId);
|
||||
if (!jornada) throw new Error('Jornada no encontrada');
|
||||
if (jornada.estado !== 'ABIERTA') throw new Error('No se pueden registrar gastos en jornada cerrada');
|
||||
const gastoId = await this.gastosService.registrarGasto(gasto);
|
||||
await db.jornadas.put({
|
||||
...jornada,
|
||||
gastoIds: [...(jornada.gastoIds || []), gasto.id],
|
||||
updatedAt: new Date()
|
||||
});
|
||||
return gastoId;
|
||||
}
|
||||
|
||||
async cerrarJornada(jornadaId: string, cuadreId?: string): Promise<string> {
|
||||
const jornada: any = await db.jornadas.get(jornadaId);
|
||||
if (!jornada) throw new Error('Jornada no encontrada');
|
||||
if (jornada.estado === 'CERRADA') throw new Error('Jornada ya cerrada');
|
||||
|
||||
let cid = cuadreId;
|
||||
if (!cid) {
|
||||
const cuadreParcial = await this.cuadreService.calcularCuadreDelDia(jornada.fecha, jornada.baseInicial);
|
||||
const cuadre: CuadreCaja = {
|
||||
id: crypto.randomUUID(),
|
||||
fecha: jornada.fecha,
|
||||
baseInicial: jornada.baseInicial,
|
||||
totalVentasEfectivo: cuadreParcial.totalVentasEfectivo || 0,
|
||||
totalVentasQR: cuadreParcial.totalVentasQR || 0,
|
||||
totalGastosEfectivo: cuadreParcial.totalGastosEfectivo || 0,
|
||||
totalGastosQR: cuadreParcial.totalGastosQR || 0,
|
||||
saldoEsperadoEfectivo: cuadreParcial.saldoEsperadoEfectivo || 0,
|
||||
totalVentas: cuadreParcial.totalVentas || 0,
|
||||
createdAt: new Date()
|
||||
} as CuadreCaja;
|
||||
cid = await this.cuadreService.guardarCuadre(cuadre);
|
||||
}
|
||||
|
||||
await db.jornadas.put({
|
||||
...jornada,
|
||||
estado: 'CERRADA',
|
||||
fechaCierre: new Date(),
|
||||
cuadreId: cid,
|
||||
updatedAt: new Date()
|
||||
});
|
||||
return cid;
|
||||
}
|
||||
|
||||
async getResumenJornada(jornadaId: string) {
|
||||
const jornada: any = await db.jornadas.get(jornadaId);
|
||||
if (!jornada) return null;
|
||||
const ventas = jornada.ventaIds?.length ? await db.ventas.where('id').anyOf(jornada.ventaIds).toArray() : [];
|
||||
const gastos = jornada.gastoIds?.length ? await db.gastos.where('id').anyOf(jornada.gastoIds).toArray() : [];
|
||||
const totalVentas = ventas.reduce((acc: number, v: any) => acc + v.total, 0);
|
||||
const totalGastos = gastos.reduce((acc: number, g: any) => acc + g.monto, 0);
|
||||
return { jornada, ventas, gastos, totalVentas, totalGastos };
|
||||
}
|
||||
}
|
||||
39
packages/services/src/__tests__/jornada.usecase.test.ts
Normal file
39
packages/services/src/__tests__/jornada.usecase.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { JornadaService } from '../JornadaService';
|
||||
import { db } from '@toldo/data';
|
||||
|
||||
describe('Caso de uso jornada', () => {
|
||||
const service = new JornadaService();
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.jornadas.clear();
|
||||
await db.ventas.clear();
|
||||
await db.ventaLineas.clear();
|
||||
await db.gastos.clear();
|
||||
await db.cuadres.clear();
|
||||
await db.productos.clear();
|
||||
});
|
||||
|
||||
it('abrir jornada, registrar venta y gasto, cerrar con cuadre', async () => {
|
||||
await db.productos.add({ id: 'p1', productorId: 'pr1', nombrePresentacion: 'Café 500g', precioUnitario: 15000, activo: true } as any);
|
||||
const jid = await service.abrirJornada(['Ana', 'Luis'], 100000);
|
||||
expect(jid).toBeTruthy();
|
||||
|
||||
const ventaId = crypto.randomUUID();
|
||||
await service.registrarVentaEnJornada(jid, { id: ventaId, fecha: new Date(), subtotal: 30000, total: 30000, createdAt: new Date() } as any, [
|
||||
{ id: crypto.randomUUID(), ventaId, productoId: 'p1', cantidad: 2, precioUnitario: 15000, subtotal: 30000, formaPago: 'EFECTIVO' } as any
|
||||
]);
|
||||
|
||||
await service.registrarGastoEnJornada(jid, { id: crypto.randomUUID(), fecha: new Date(), concepto: 'Bolsas', monto: 5000, formaPago: 'EFECTIVO', createdAt: new Date() } as any);
|
||||
|
||||
const resumen = await service.getResumenJornada(jid);
|
||||
expect(resumen?.totalVentas).toBe(30000);
|
||||
expect(resumen?.totalGastos).toBe(5000);
|
||||
|
||||
const cuadreId = await service.cerrarJornada(jid);
|
||||
expect(cuadreId).toBeTruthy();
|
||||
|
||||
const abierta = await service.getJornadaAbierta();
|
||||
expect(abierta).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -5,3 +5,4 @@ export * from './inventario/InventarioService';
|
||||
export * from './cuadre/CuadreService';
|
||||
export * from './reportes/ReportesService';
|
||||
export * from './export-import/ExportImportService';
|
||||
export * from './JornadaService';
|
||||
|
||||
Reference in New Issue
Block a user