2026-10-13 20:17:52 -05:00
|
|
|
|
import { describe, expect, it } from 'vitest';
|
2026-10-14 14:37:01 -05:00
|
|
|
|
import { DOCUMENT_LINE_COLUMNS, ITEMS_MULTI_ADD, lineColumnsFor, newDocumentLine, recalcLine, uomEntryOf } from './line-columns';
|
2026-10-13 20:17:52 -05:00
|
|
|
|
|
|
|
|
|
|
describe('recalcLine', () => {
|
|
|
|
|
|
it('total = cantidad × precio, redondeado a 2 decimales', () => {
|
|
|
|
|
|
expect(recalcLine({ Quantity: 5, Price: 10000 })['LineTotal']).toBe(50000);
|
|
|
|
|
|
expect(recalcLine({ Quantity: 3, Price: 0.1 })['LineTotal']).toBe(0.3);
|
|
|
|
|
|
expect(recalcLine({ Quantity: 1.5, Price: 19.99 })['LineTotal']).toBe(29.99);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('aplica el % de descuento', () => {
|
|
|
|
|
|
expect(recalcLine({ Quantity: 2, Price: 100, DiscountPercent: 10 })['LineTotal']).toBe(180);
|
|
|
|
|
|
expect(recalcLine({ Quantity: 2, Price: 100, DiscountPercent: 100 })['LineTotal']).toBe(0);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('valores vacíos o no numéricos cuentan como 0 y se conserva el resto de la línea', () => {
|
|
|
|
|
|
const line = recalcLine({ ItemCode: 'A', Quantity: null, Price: 'abc' });
|
|
|
|
|
|
expect(line['LineTotal']).toBe(0);
|
|
|
|
|
|
expect(line['ItemCode']).toBe('A');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('columnas estándar de línea', () => {
|
|
|
|
|
|
const keys = DOCUMENT_LINE_COLUMNS.map((c) => c.key);
|
|
|
|
|
|
|
|
|
|
|
|
it('son las de una línea de documento de SAP (con TaxCode)', () => {
|
|
|
|
|
|
expect(keys).toEqual(['ItemCode', 'ItemDescription', 'Quantity', 'WarehouseCode', 'Price', 'DiscountPercent', 'TaxCode', 'LineTotal']);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('el total es de solo lectura y artículo y cantidad son obligatorios', () => {
|
|
|
|
|
|
expect(DOCUMENT_LINE_COLUMNS.find((c) => c.key === 'LineTotal')?.readonly).toBe(true);
|
|
|
|
|
|
expect(DOCUMENT_LINE_COLUMNS.filter((c) => c.required).map((c) => c.key)).toEqual(['ItemCode', 'Quantity']);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('artículo, almacén e impuesto tienen lista de valores sobre recursos registrados', () => {
|
|
|
|
|
|
const resources = DOCUMENT_LINE_COLUMNS.filter((c) => c.lookup).map((c) => c.lookup!.config.resource);
|
|
|
|
|
|
expect(resources).toEqual(['Items', 'Warehouses', 'SalesTaxCodes']);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('elegir un artículo rellena código y descripción sin perder el resto', () => {
|
|
|
|
|
|
const apply = DOCUMENT_LINE_COLUMNS[0].lookup!.apply;
|
|
|
|
|
|
expect(apply({ ItemCode: 'I1', ItemName: 'Disco' }, { Quantity: 3 })).toMatchObject({ ItemCode: 'I1', ItemDescription: 'Disco', Quantity: 3 });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('elegir almacén o impuesto solo cambia ese campo', () => {
|
|
|
|
|
|
const wh = DOCUMENT_LINE_COLUMNS.find((c) => c.key === 'WarehouseCode')!.lookup!.apply;
|
|
|
|
|
|
const tax = DOCUMENT_LINE_COLUMNS.find((c) => c.key === 'TaxCode')!.lookup!.apply;
|
|
|
|
|
|
expect(wh({ WarehouseCode: '01', WarehouseName: 'Principal' }, { ItemCode: 'A' })).toEqual({ ItemCode: 'A', WarehouseCode: '01' });
|
|
|
|
|
|
expect(tax({ Code: 'IVA', Rate: 19 }, { ItemCode: 'A' })).toEqual({ ItemCode: 'A', TaxCode: 'IVA' });
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('línea nueva y "catálogo completo"', () => {
|
|
|
|
|
|
it('la línea nueva trae cantidad 1 y total 0', () => {
|
2026-10-14 11:06:58 -05:00
|
|
|
|
expect(newDocumentLine()).toMatchObject({ Quantity: 1, Price: null, LineTotal: 0, ItemCode: '' });
|
2026-10-13 20:17:52 -05:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('cada artículo elegido se convierte en una línea nueva', () => {
|
|
|
|
|
|
expect(ITEMS_MULTI_ADD.toLine({ ItemCode: 'I9', ItemName: 'Cable' })).toMatchObject({ ItemCode: 'I9', ItemDescription: 'Cable', Quantity: 1 });
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-10-13 22:00:54 -05:00
|
|
|
|
|
|
|
|
|
|
describe('lineColumnsFor', () => {
|
|
|
|
|
|
it('ventas conserva las columnas; compras cambia el maestro de impuestos', () => {
|
|
|
|
|
|
expect(lineColumnsFor('SalesTaxCodes')).toBe(DOCUMENT_LINE_COLUMNS);
|
|
|
|
|
|
const tax = lineColumnsFor('PurchaseTaxCodes').find((c) => c.key === 'TaxCode');
|
|
|
|
|
|
expect(tax?.lookup?.config.resource).toBe('PurchaseTaxCodes');
|
|
|
|
|
|
expect(DOCUMENT_LINE_COLUMNS.find((c) => c.key === 'TaxCode')?.lookup?.config.resource).toBe('SalesTaxCodes');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-10-14 14:37:01 -05:00
|
|
|
|
|
|
|
|
|
|
describe('uomEntryOf', () => {
|
|
|
|
|
|
it('usa la unidad de compra por defecto y, si no hay, la de inventario', () => {
|
|
|
|
|
|
expect(uomEntryOf({ UoMGroupEntry: 1, DefaultPurchasingUoMEntry: 3, InventoryUoMEntry: 1 })).toBe(3);
|
|
|
|
|
|
expect(uomEntryOf({ UoMGroupEntry: 1, DefaultPurchasingUoMEntry: null, InventoryUoMEntry: 1 })).toBe(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
it('un artículo de unidad manual (sin grupo) no lleva unidad: SAP no la admite', () => {
|
|
|
|
|
|
expect(uomEntryOf({ UoMGroupEntry: -1, InventoryUoMEntry: -1 })).toBeUndefined();
|
|
|
|
|
|
expect(uomEntryOf({ InventoryUoMEntry: 1 })).toBeUndefined();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|