82 lines
3.8 KiB
TypeScript
82 lines
3.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|||
|
|
import { EntradaAbierta, LineaPlanificacion } from '../models/planificacion.js';
|
||
|
|
import { asignarFifo, consolidarDocumentos, extraerLineasAplicables, puedeCambiarSeleccion } from './fifo.js';
|
||
|
|
|
||
|
|
function entrada(docEntry: number, docDate: string, openQty: number, cardCode = 'P1'): EntradaAbierta {
|
||
|
|
return { docEntry, lineNum: 0, docNum: docEntry + 1000, docDate, cardCode, cardName: `Prov ${cardCode}`, openQty };
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('asignarFifo', () => {
|
||
|
|
it('reparte la cantidad desde la Entrada más antigua', () => {
|
||
|
|
const entradas = new Map([['A001', [entrada(2, '2026-02-01', 10), entrada(1, '2026-01-01', 4)]]]);
|
||
|
|
const { lineas, advertencias } = asignarFifo([{ itemCode: 'a001', itemName: 'Art', quantity: 6, onHand: 20 }], entradas);
|
||
|
|
|
||
|
|
expect(advertencias).toEqual([]);
|
||
|
|
expect(lineas.map((l) => [l.tipoFila, l.baseEntry, l.allocatedQty])).toEqual([
|
||
|
|
['F', null, null],
|
||
|
|
['S', 1, 4],
|
||
|
|
['S', 2, 2],
|
||
|
|
]);
|
||
|
|
expect(lineas[1]).toMatchObject({ itemCode: 'a001', applied: true, baseType: 20, baseRef: 1001 });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('omite artículos sin stock suficiente', () => {
|
||
|
|
const { lineas, advertencias } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 6, onHand: 5 }], new Map());
|
||
|
|
expect(lineas).toEqual([]);
|
||
|
|
expect(advertencias[0]).toContain('sin stock');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('advierte cuando las Entradas no alcanzan', () => {
|
||
|
|
const entradas = new Map([['A001', [entrada(1, '2026-01-01', 2)]]]);
|
||
|
|
const { lineas, advertencias } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 5, onHand: 5 }], entradas);
|
||
|
|
expect(lineas).toHaveLength(2);
|
||
|
|
expect(advertencias[0]).toContain('faltan 3');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('extraerLineasAplicables / puedeCambiarSeleccion', () => {
|
||
|
|
const base = asignarFifo(
|
||
|
|
[{ itemCode: 'A001', itemName: 'Art', quantity: 5, onHand: 5 }],
|
||
|
|
new Map([['A001', [entrada(1, '2026-01-01', 2), entrada(2, '2026-01-02', 2), entrada(3, '2026-01-03', 2)]]]),
|
||
|
|
).lineas;
|
||
|
|
|
||
|
|
it('devuelve solo Hijos marcados sin documento destino', () => {
|
||
|
|
const lineas: LineaPlanificacion[] = base.map((l) => ({ ...l }));
|
||
|
|
lineas[2].applied = false;
|
||
|
|
lineas[3].targetEntry = 99;
|
||
|
|
expect(extraerLineasAplicables(lineas).map((l) => l.baseEntry)).toEqual([1]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('hereda el ItemCode del Padre si el Hijo no lo trae', () => {
|
||
|
|
const lineas = base.map((l) => (l.tipoFila === 'S' ? { ...l, itemCode: '' } : l));
|
||
|
|
expect(extraerLineasAplicables(lineas)[0].itemCode).toBe('A001');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('bloquea la selección de líneas con documento destino', () => {
|
||
|
|
expect(puedeCambiarSeleccion(base[1])).toBe(true);
|
||
|
|
expect(puedeCambiarSeleccion({ ...base[1], targetEntry: 10 })).toBe(false);
|
||
|
|
expect(puedeCambiarSeleccion(base[0])).toBe(false);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('consolidarDocumentos', () => {
|
||
|
|
it('agrupa por proveedor y corta en lotes', () => {
|
||
|
|
const entradas = new Map([
|
||
|
|
['A001', [entrada(1, '2026-01-01', 1, 'P1'), entrada(2, '2026-01-02', 1, 'P2'), entrada(3, '2026-01-03', 1, 'P1'), entrada(4, '2026-01-04', 1, 'P1')]],
|
||
|
|
]);
|
||
|
|
const { lineas } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 4, onHand: 4 }], entradas);
|
||
|
|
const docs = consolidarDocumentos(extraerLineasAplicables(lineas), 2);
|
||
|
|
|
||
|
|
expect(docs.map((d) => [d.numero, d.cardCode, d.lineas.map((l) => l.baseEntry)])).toEqual([
|
||
|
|
[1, 'P1', [1, 3]],
|
||
|
|
[2, 'P1', [4]],
|
||
|
|
[3, 'P2', [2]],
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('usa 11 líneas si el máximo no es válido', () => {
|
||
|
|
const entradas = new Map([['A001', Array.from({ length: 12 }, (_, i) => entrada(i + 1, '2026-01-01', 1))]]);
|
||
|
|
const { lineas } = asignarFifo([{ itemCode: 'A001', itemName: 'Art', quantity: 12, onHand: 12 }], entradas);
|
||
|
|
expect(consolidarDocumentos(extraerLineasAplicables(lineas), 0).map((d) => d.lineas.length)).toEqual([11, 1]);
|
||
|
|
});
|
||
|
|
});
|