feat(web): columnas de las listas de valores (cliente, artículo, almacén, impuesto) configurables por documento

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-14 15:39:53 -05:00
1 parent b3b47c34a3
commit 0d5a3074c8
6 files changed
+141 -12

No files matched your search

@@ -1,4 +1,5 @@
import type { GridColumn, GridLine } from '../../shared/grid/grid.types';
import type { ListColumn } from '../../shared/list/list.types';
import type { VhLookup } from '../../shared/value-help/value-help.types';
import type { DocRecord, LinesSpec } from './document-model';
import { DOCUMENT_LINE_COLUMNS, lineColumnsFor, lineFieldsOf, newDocumentLine, recalcLine, uomEntryOf } from './line-columns';
@@ -25,6 +26,12 @@ export type HeaderField =
| { type: 'folio'; label: string; prefixKey: string; numberKey: string }
| { type: 'computed'; label: string; source: ComputedSource };
/** Las listas de valores (ventanas modales) de un formulario. */
export type LookupName = 'partner' | 'item' | 'warehouse' | 'tax';
/** Columnas que muestra cada lista de valores; la primera es el código que se escribe en el campo. */
export type LookupColumns = Partial<Record<LookupName, readonly ListColumn[]>>;
export interface FieldColumns {
/** Dos columnas como máximo (regla del proyecto base). */
left: readonly HeaderField[];
@@ -47,6 +54,8 @@ export interface DocumentLayout {
newLine: (header: DocRecord) => GridLine;
/** Recalcula una línea tras un cambio (total = cantidad × precio…). */
recalc?: (line: GridLine, changedKey: string) => GridLine;
/** Columnas de las listas de valores de este documento; las que falten usan las del base. */
lookups?: LookupColumns;
/** Una vez contabilizado ya no se modifica (SAP no permite cambiar entradas, salidas ni transferencias). */
immutable?: boolean;
/** Ajustes a la cabecera antes de crear. */
@@ -91,6 +100,45 @@ export const itemColumn: GridColumn = {
/** Lista de almacenes de la cabecera (la misma de las líneas). */
export const WAREHOUSE_LOOKUP: VhLookup = warehouseColumn.lookup!.config;
/** Columnas del base de la lista de clientes y proveedores. */
export const PARTNER_COLUMNS: readonly ListColumn[] = [
{ key: 'CardCode', title: 'Código', width: '130px', mono: true },
{ key: 'CardName', title: 'Nombre' },
];
/** Columna de línea que abre cada lista de valores. */
const LOOKUP_OF_COLUMN: Readonly<Record<string, LookupName>> = {
ItemCode: 'item',
WarehouseCode: 'warehouse',
FromWarehouseCode: 'warehouse',
TaxCode: 'tax',
};
const defaultLookupColumns = (layout: DocumentLayout, name: LookupName): readonly ListColumn[] => {
if (layout.lookups?.[name]) return layout.lookups[name]!;
if (name === 'partner') return PARTNER_COLUMNS;
return layout.columns.find((c) => LOOKUP_OF_COLUMN[c.key] === name)?.lookup?.config.columns ?? [];
};
/**
* Cambia las columnas de las listas de valores de un documento. `replace` las sustituye por las
* indicadas (el base lo usa); `add` las suma a las que ya tiene (lo usan las extensiones de cliente).
* El recurso, el filtro y el orden de cada lista no cambian.
*/
export function withLookupColumns(layout: DocumentLayout, changes: LookupColumns, mode: 'replace' | 'add'): DocumentLayout {
const next: Partial<Record<LookupName, readonly ListColumn[]>> = { ...layout.lookups };
for (const name of Object.keys(changes) as LookupName[]) {
const given = changes[name] ?? [];
next[name] = mode === 'add' ? [...defaultLookupColumns(layout, name), ...given] : given;
}
const columns = layout.columns.map((c) => {
const name = LOOKUP_OF_COLUMN[c.key];
const cols = name ? next[name] : undefined;
return cols && c.lookup ? { ...c, lookup: { ...c.lookup, config: { ...c.lookup.config, columns: cols } } } : c;
});
return { ...layout, columns, lookups: next };
}
export const blankLine = (): GridLine => ({ ItemCode: '', ItemDescription: '', Quantity: 1, WarehouseCode: '' });
// --- documentos con socio de negocio (ventas y compras) -------------------------------------
@@ -110,9 +158,11 @@ export function marketingLayout(opts: {
right?: readonly HeaderField[];
header?: FieldColumns;
lineColumns?: readonly GridColumn[];
/** Columnas de las listas de valores (cliente, artículo, almacén, impuesto) de este documento. */
lookups?: LookupColumns;
}): DocumentLayout {
const columns = lineColumnsFor(opts.taxResource, opts.lineColumns);
return {
const layout: DocumentLayout = {
lines: { key: 'DocumentLines', fields: lineFieldsOf(columns), base: true },
header: opts.header ?? {
left: [
@@ -139,6 +189,7 @@ export function marketingLayout(opts: {
recalc: recalcLine,
requiredLine: [],
};
return opts.lookups ? withLookupColumns(layout, opts.lookups, 'replace') : layout;
}
// --- consultas sobre un layout --------------------------------------------------------------