refactor(web): el formulario base recibe de cada pantalla los campos de cabecera, pie y línea

Todas las pantallas declaran su DocumentLayout (catálogo FIELDS): ventas y compras dejan de tener la
cabecera escrita en el HTML; el folio solo lo llevan los documentos tributarios.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-14 15:10:40 -05:00
1 parent c734dcbd0a
commit 8b0b8afc87
9 files changed
+320 -257

No files matched your search

@@ -4,7 +4,7 @@ import { type ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router, convertToParamMap, provideRouter } from '@angular/router';
import { of } from 'rxjs';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { LAYOUTS, headerKeysOf } from './document-layouts';
import { LAYOUTS, hasFolio, headerKeysOf } from './document-layouts';
import { createPayload, updatePayload, validateLayout } from './document-model';
import { DocumentFormPage } from './document-form.page';
import { DOCUMENT_SCREENS } from './document-screens';
@@ -12,18 +12,33 @@ import { DOCUMENT_SCREENS } from './document-screens';
describe('modelo de los formularios sin socio', () => {
const transfer = LAYOUTS.transferRequest;
it('cada pantalla de inventario y la solicitud de compra tienen su formulario', () => {
const withLayout = DOCUMENT_SCREENS.filter((s) => s.layout).map((s) => s.resource);
expect(withLayout.sort()).toEqual(
it('cada pantalla tiene su formulario, declarado por ella', () => {
expect(DOCUMENT_SCREENS.every((s) => s.layout)).toBe(true);
const withPartner = DOCUMENT_SCREENS.filter((s) => s.partner).map((s) => s.resource);
const without = DOCUMENT_SCREENS.filter((s) => !s.partner).map((s) => s.resource);
expect(withPartner).toHaveLength(12);
expect(without.sort()).toEqual(
['InventoryGenEntries', 'InventoryGenExits', 'InventoryTransferRequests', 'PurchaseRequests', 'StockTransfers'].sort(),
);
expect(DOCUMENT_SCREENS.filter((s) => s.layout && s.partner)).toHaveLength(0);
});
it('la cabecera tiene como máximo dos columnas y no usa campos de usuario', () => {
for (const l of Object.values(LAYOUTS)) {
for (const k of headerKeysOf(l)) expect(k.startsWith('U_')).toBe(false);
for (const c of l.columns) expect(c.key.startsWith('U_')).toBe(false);
it('cada formulario declara solo sus campos: la orden de venta no lleva folio y la factura sí', () => {
const by = (r: string) => DOCUMENT_SCREENS.find((s) => s.resource === r)!.layout;
expect(hasFolio(by('Orders'))).toBe(false);
expect(hasFolio(by('PurchaseQuotations'))).toBe(false);
for (const r of ['Invoices', 'PurchaseInvoices', 'CreditNotes', 'PurchaseCreditNotes', 'DeliveryNotes', 'PurchaseDeliveryNotes']) {
expect(hasFolio(by(r))).toBe(true);
}
expect(headerKeysOf(by('Invoices'))).toEqual(expect.arrayContaining(['FolioPrefixString', 'FolioNumber']));
expect(headerKeysOf(by('Orders'))).not.toContain('FolioNumber');
});
it('la cabecera y el pie tienen como máximo dos columnas y no usan campos de usuario', () => {
for (const s of DOCUMENT_SCREENS) {
for (const k of headerKeysOf(s.layout)) expect(k.startsWith('U_')).toBe(false);
for (const c of s.layout.columns) expect(c.key.startsWith('U_')).toBe(false);
expect(Object.keys(s.layout.header)).toEqual(['left', 'right']);
expect(Object.keys(s.layout.footer)).toEqual(['left', 'right']);
}
});