feat(web): maestros de clientes, proveedores y artículos (lista, formulario con pestañas, extensiones por cliente)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b759c7bce7
commit
9f003ee4bf
30 files changed
+1553
-23
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import { escapeODataString } from '../../core/sl/odata';
|
||||
import { type HeaderField, keysOfFields, requiredOfFields } from '../documents/document-layouts';
|
||||
import { type DocRecord, compact } from '../documents/document-model';
|
||||
import type { MasterLayout } from './master-types';
|
||||
|
||||
const isEmpty = (v: unknown): boolean => v === null || v === undefined || (typeof v === 'string' && v.trim() === '');
|
||||
|
||||
/** Todos los campos del formulario: los de arriba y los de cada pestaña. */
|
||||
export const masterFieldsOf = (layout: MasterLayout): HeaderField[] => [
|
||||
...layout.header.left,
|
||||
...layout.header.right,
|
||||
...layout.tabs.flatMap((t) => [...t.left, ...t.right]),
|
||||
];
|
||||
|
||||
/** Campos de SAP que se leen, se editan y se envían. */
|
||||
export const masterKeysOf = (layout: MasterLayout): readonly string[] => keysOfFields(masterFieldsOf(layout));
|
||||
|
||||
/** Campos obligatorios con el texto del aviso ("Indique el código."). */
|
||||
export const masterRequiredOf = (layout: MasterLayout): { key: string; label: string }[] => requiredOfFields(masterFieldsOf(layout));
|
||||
|
||||
/** Campos que no se pueden cambiar una vez creado el registro (el código). */
|
||||
export const masterImmutableOf = (layout: MasterLayout): ReadonlySet<string> =>
|
||||
new Set(masterFieldsOf(layout).flatMap((f) => (f.type === 'text' && f.immutable ? [f.key] : [])));
|
||||
|
||||
/** Valores editables de un registro de SAP (los vacíos quedan como texto vacío). */
|
||||
export function pickMaster(doc: DocRecord | undefined, keys: readonly string[], blank: DocRecord): DocRecord {
|
||||
if (!doc) return blank;
|
||||
return Object.fromEntries(keys.map((k) => [k, doc[k] ?? '']));
|
||||
}
|
||||
|
||||
/** Errores que se pueden detectar sin preguntarle a SAP. */
|
||||
export function validateMaster(header: DocRecord, required: readonly { key: string; label: string }[]): string[] {
|
||||
return required.filter((f) => isEmpty(header[f.key])).map((f) => `Indique ${f.label.toLowerCase()}.`);
|
||||
}
|
||||
|
||||
/** Cuerpo para crear: cabecera sin campos vacíos más los valores fijos de la pantalla. */
|
||||
export const createMasterPayload = (header: DocRecord, fixed?: DocRecord): DocRecord => ({ ...compact(header), ...fixed });
|
||||
|
||||
export type MasterNavTarget = 'first' | 'prev' | 'next' | 'last';
|
||||
|
||||
/** Consulta de un solo registro para ir al primero / anterior / siguiente / último por su clave de texto. */
|
||||
export function masterNavigationQuery(
|
||||
target: MasterNavTarget,
|
||||
keyField: string,
|
||||
current: string | null,
|
||||
): { $filter?: string; $orderby: string } {
|
||||
const asc = `${keyField} asc`;
|
||||
const desc = `${keyField} desc`;
|
||||
const quoted = current === null ? '' : `'${escapeODataString(current)}'`;
|
||||
switch (target) {
|
||||
case 'first':
|
||||
return { $orderby: asc };
|
||||
case 'last':
|
||||
return { $orderby: desc };
|
||||
case 'prev':
|
||||
return current === null ? { $orderby: desc } : { $filter: `${keyField} lt ${quoted}`, $orderby: desc };
|
||||
case 'next':
|
||||
return current === null ? { $orderby: asc } : { $filter: `${keyField} gt ${quoted}`, $orderby: asc };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user