Files
basic-erp/apps/api/src/sapdb/user-fields.ts
T

82 lines
3.3 KiB
TypeScript
Raw Normal View History

import type { SapDbAdapter, SapDbEngine } from './sapdb.types.js';
/** Lo que SAP sabe de un campo de usuario: su tipo, sus valores válidos y la tabla de usuario a la que está enlazado. */
export interface UserFieldInfo {
table: string;
alias: string;
description: string;
/** Tipo de SAP: `A` alfanumérico, `N` numérico, `D` fecha… */
type: string;
size: number;
/** Valores válidos del campo (vacío si acepta cualquier valor). */
validValues: { value: string; description: string }[];
/** Tabla de usuario enlazada (`SIS_REGSAN`), si el campo está vinculado a una. */
linkedTable: string | null;
/** Filas (`Code` y `Name`) de la tabla enlazada. */
linkedRows: { code: string; name: string }[];
/** La tabla enlazada tiene más filas de las que se entregan. */
linkedTruncated: boolean;
}
/** Una tabla de SAP (`OBTN`, `@MI_TABLA`) y el alias de un campo de usuario (`SIS_ESTLOT`, sin el `U_`). */
export const TABLE_ID = /^@?[A-Za-z0-9_]{1,30}$/;
export const FIELD_ALIAS = /^[A-Za-z0-9_]{1,30}$/;
/** Una tabla enlazada se escribe en el SQL entre comillas: solo letras, dígitos y «_». */
const LINKED = /^[A-Za-z0-9_]{1,30}$/;
interface Sql {
info: string;
values: string;
linked: (table: string) => string;
}
/** El SQL de cada motor (`CUFD` define los campos de usuario y `UFD1` sus valores válidos; existen igual en HANA y MSSQL). */
const SQL: Record<SapDbEngine, Sql> = {
hana: {
info: 'SELECT "FieldID", "Descr", "TypeID", "EditSize", "RTable" FROM "CUFD" WHERE "TableID" = ? AND "AliasID" = ?',
values: 'SELECT "FldValue", "Descr" FROM "UFD1" WHERE "TableID" = ? AND "FieldID" = ? ORDER BY "IndexID"',
linked: (t) => `SELECT "Code", "Name" FROM "@${t}" ORDER BY "Code"`,
},
};
const text = (v: unknown): string => (v === null || v === undefined ? '' : String(v));
/**
* Lee de la base la definición de un campo de usuario. Devuelve `null` si el campo no existe. La tabla enlazada sale de
* la propia definición del campo (no la elige quien llama), así que no se puede leer una tabla cualquiera.
*/
export async function readUserField(
adapter: SapDbAdapter,
engine: SapDbEngine,
table: string,
alias: string,
opts: { timeoutMs: number; maxRows: number },
): Promise<UserFieldInfo | null> {
const sql = SQL[engine];
const [field] = await adapter.query(sql.info, [table, alias], opts);
if (!field) return null;
const values = await adapter.query(sql.values, [table, field['FieldID']], opts);
const linkedTable = text(field['RTable']) || null;
let linkedRows: UserFieldInfo['linkedRows'] = [];
let linkedTruncated = false;
if (linkedTable && LINKED.test(linkedTable)) {
try {
const rows = await adapter.query(sql.linked(linkedTable), [], opts);
linkedTruncated = rows.length > opts.maxRows;
linkedRows = rows.slice(0, opts.maxRows).map((r) => ({ code: text(r['Code']), name: text(r['Name']) }));
} catch {
/* el enlace no es a una tabla de usuario con Code y Name: el campo se trata como de valores libres */
}
}
return {
table,
alias,
description: text(field['Descr']),
type: text(field['TypeID']),
size: Number(field['EditSize'] ?? 0) || 0,
validValues: values.map((v) => ({ value: text(v['FldValue']), description: text(v['Descr']) })),
linkedTable,
linkedRows,
linkedTruncated,
};
}