feat(web): fila del flete (gastos adicionales) y toolbar de ancho completo; la configuración de pantallas se carga en diferido

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-14 16:09:06 -05:00
1 parent d3ab97cd2e
commit d6c127627f
14 files changed
+360 -27

No files matched your search

@@ -9,6 +9,8 @@ import {
taxRateMap,
updatePayload,
validateLayout,
freightOf,
freightPayload,
} from './document-model';
const line = (over: Record<string, unknown> = {}) => ({
@@ -35,12 +37,12 @@ describe('computeTotals', () => {
it('suma líneas e impuesto por tasa', () => {
const t = computeTotals([line(), line({ TaxCode: 'EXE', LineTotal: 100 })], rates, 0);
expect(t).toEqual({ subtotal: 300, discount: 0, net: 300, tax: 38, total: 338 });
expect(t).toEqual({ subtotal: 300, discount: 0, net: 300, tax: 38, freight: 0, total: 338 });
});
it('aplica el descuento de cabecera antes del impuesto', () => {
const t = computeTotals([line()], rates, 10);
expect(t).toEqual({ subtotal: 200, discount: 20, net: 180, tax: 34.2, total: 214.2 });
expect(t).toEqual({ subtotal: 200, discount: 20, net: 180, tax: 34.2, freight: 0, total: 214.2 });
});
it('un código de impuesto desconocido no suma impuesto', () => {
@@ -48,7 +50,7 @@ describe('computeTotals', () => {
});
it('sin líneas, todo en cero', () => {
expect(computeTotals([], rates, 0)).toEqual({ subtotal: 0, discount: 0, net: 0, tax: 0, total: 0 });
expect(computeTotals([], rates, 0)).toEqual({ subtotal: 0, discount: 0, net: 0, tax: 0, freight: 0, total: 0 });
});
});
@@ -151,3 +153,40 @@ describe('navigationQuery', () => {
expect(navigationQuery('next', null)).toEqual({ $orderby: 'DocEntry asc' });
});
});
describe('flete (gastos adicionales)', () => {
const doc = (rows: Record<string, unknown>[]) => ({ DocumentAdditionalExpenses: rows });
it('suma el flete al total del documento', () => {
const rates = taxRateMap([{ Code: 'IVA', Rate: 19 }]);
const t = computeTotals([line({ LineTotal: 100, TaxCode: 'IVA' })], rates, 0, 25);
expect(t).toMatchObject({ subtotal: 100, tax: 19, freight: 25, total: 144 });
});
it('lee el flete de SAP: código, importe y si se puede editar', () => {
expect(freightOf(doc([{ ExpenseCode: 2, LineTotal: 1500 }]))).toEqual({ expense: '2', amount: 1500, editable: true });
expect(freightOf(undefined)).toEqual({ expense: '', amount: 0, editable: true });
expect(freightOf(doc([{ ExpenseCode: 1, LineTotal: 10 }, { ExpenseCode: 2, LineTotal: 5 }])).editable).toBe(false);
});
it('al crear arma DocumentAdditionalExpenses con el gasto por defecto si no se eligió', () => {
const out = freightPayload({ CardCode: 'C1', FreightAmount: 1500, FreightExpense: '' }, { FreightAmount: 1500, FreightExpense: '' }, '2', 'create');
expect(out.body).toEqual({ CardCode: 'C1', DocumentAdditionalExpenses: [{ ExpenseCode: 2, LineTotal: 1500 }] });
expect(out.replace).toBe(true);
});
it('al crear sin importe no envía flete', () => {
const out = freightPayload({ CardCode: 'C1', FreightAmount: 0 }, { FreightAmount: 0 }, '2', 'create');
expect(out.body).toEqual({ CardCode: 'C1' });
expect(out.replace).toBe(false);
});
it('al modificar solo envía el flete si cambió, y con importe 0 lo quita', () => {
expect(freightPayload({ Comments: 'x' }, { FreightAmount: 10 }, '2', 'update', true)).toEqual({ body: { Comments: 'x' }, replace: false });
const changed = freightPayload({ FreightAmount: 300 }, { FreightAmount: 300, FreightExpense: '3' }, '2', 'update', true);
expect(changed).toEqual({ body: { DocumentAdditionalExpenses: [{ ExpenseCode: 3, LineTotal: 300 }] }, replace: true });
const cleared = freightPayload({ FreightAmount: 0 }, { FreightAmount: 0, FreightExpense: '3' }, '2', 'update', true);
expect(cleared).toEqual({ body: { DocumentAdditionalExpenses: [] }, replace: true });
expect(freightPayload({ FreightAmount: 0 }, { FreightAmount: 0 }, '2', 'update', false).body).toEqual({});
});
});