feat: parámetros ocultos (títulos y separadores) de los reportes Crystal

This commit is contained in:
George committed 2026-10-19 17:46:44 -05:00
1 parent 2a29c63e7d
commit 03ccd97f99
3 files changed
+13 -1

No files matched your search

@@ -14,6 +14,7 @@ const paramSchema = z.object({
label: text(120).min(1), label: text(120).min(1),
kind: z.enum(['text', 'number', 'date', 'select', 'lookup']), kind: z.enum(['text', 'number', 'date', 'select', 'lookup']),
required: z.boolean(), required: z.boolean(),
hidden: z.boolean().optional(),
range: z.boolean().optional(), range: z.boolean().optional(),
multiple: z.boolean().optional(), multiple: z.boolean().optional(),
defaultValue: text(200).optional(), defaultValue: text(200).optional(),
+6
View File
@@ -8,11 +8,13 @@ describe('paramsFromLoadCr', () => {
{ name: 'Dockey@', type: 'xsd:decimal', description: 'Especificar Dockey@:', isOptionalPrompt: 'false', valueRangeKind: 'Discrete', allowMultiValue: 'false' }, { name: 'Dockey@', type: 'xsd:decimal', description: 'Especificar Dockey@:', isOptionalPrompt: 'false', valueRangeKind: 'Discrete', allowMultiValue: 'false' },
{ name: 'BP@From OCRD', type: 'xsd:string', description: 'Socios de negocios', isOptionalPrompt: 'true', allowMultiValue: 'true', defaultValues: ['C1'] }, { name: 'BP@From OCRD', type: 'xsd:string', description: 'Socios de negocios', isOptionalPrompt: 'true', allowMultiValue: 'true', defaultValues: ['C1'] },
{ type: 'xsd:string' }, { type: 'xsd:string' },
{ name: 'Title_Items@Title', type: 'xsd:string', description: 'Artículos', isOptionalPrompt: 'false' },
]); ]);
expect(params).toEqual([ expect(params).toEqual([
{ name: 'PWZRunDate', type: 'xsd:date', label: 'Fecha de ejecución de pago', kind: 'date', required: false, range: true }, { name: 'PWZRunDate', type: 'xsd:date', label: 'Fecha de ejecución de pago', kind: 'date', required: false, range: true },
{ name: 'Dockey@', type: 'xsd:decimal', label: 'Dockey@', kind: 'number', required: true }, { name: 'Dockey@', type: 'xsd:decimal', label: 'Dockey@', kind: 'number', required: true },
{ name: 'BP@From OCRD', type: 'xsd:string', label: 'Socios de negocios', kind: 'text', required: false, multiple: true, defaultValue: 'C1' }, { name: 'BP@From OCRD', type: 'xsd:string', label: 'Socios de negocios', kind: 'text', required: false, multiple: true, defaultValue: 'C1' },
{ name: 'Title_Items@Title', type: 'xsd:string', label: 'Artículos', kind: 'text', required: false, hidden: true },
]); ]);
}); });
}); });
@@ -36,6 +38,10 @@ describe('exportBody', () => {
expect(r.missing).toEqual(['Cliente']); expect(r.missing).toEqual(['Cliente']);
}); });
it('los títulos del reporte no se piden ni se envían', () => {
expect(exportBody([p({ name: 'T@Title', hidden: true, required: true })], { 'T@Title': 'x' }).body).toEqual([]);
});
it('ignora valores de parámetros que no están matriculados', () => { it('ignora valores de parámetros que no están matriculados', () => {
expect(exportBody([p({ name: 'A' })], { A: '1', Otro: '2' }).body.map((b) => b.name)).toEqual(['A']); expect(exportBody([p({ name: 'A' })], { A: '1', Otro: '2' }).body.map((b) => b.name)).toEqual(['A']);
}); });
+6 -1
View File
@@ -23,6 +23,8 @@ export interface GatewayParam {
placeholder?: string; placeholder?: string;
/** Opciones de una lista fija (`kind = select`). */ /** Opciones de una lista fija (`kind = select`). */
options?: { value: string; label: string }[]; options?: { value: string; label: string }[];
/** Un título o separador del reporte (`Title_Items@Title`, `Sep_item@separator`): no se pide al usuario ni se envía. */
hidden?: boolean;
/** Ventana de selección (`kind = lookup`): identificador de su catálogo en la web (`socios`, `articulos`, `doc:Invoices`…). */ /** Ventana de selección (`kind = lookup`): identificador de su catálogo en la web (`socios`, `articulos`, `doc:Invoices`…). */
lookup?: string; lookup?: string;
} }
@@ -70,12 +72,14 @@ export function paramsFromLoadCr(resultSet: readonly CrystalParameter[]): Gatewa
.map((p) => { .map((p) => {
const type = p.type ?? 'xsd:string'; const type = p.type ?? 'xsd:string';
const first = p.defaultValues?.[0]; const first = p.defaultValues?.[0];
const decorative = /@(title|separator)$/i.test(p.name);
return { return {
name: p.name, name: p.name,
type, type,
label: (p.description ?? p.name).replace(/[:\s]+$/, '').replace(/^Especificar\s+/i, '') || p.name, label: (p.description ?? p.name).replace(/[:\s]+$/, '').replace(/^Especificar\s+/i, '') || p.name,
kind: /date/i.test(type) ? 'date' : NUMERIC.test(type) ? 'number' : 'text', kind: /date/i.test(type) ? 'date' : NUMERIC.test(type) ? 'number' : 'text',
required: p.isOptionalPrompt !== 'true', required: decorative ? false : p.isOptionalPrompt !== 'true',
...(decorative ? { hidden: true } : {}),
...(p.valueRangeKind === 'Range' ? { range: true } : {}), ...(p.valueRangeKind === 'Range' ? { range: true } : {}),
...(p.allowMultiValue === 'true' ? { multiple: true } : {}), ...(p.allowMultiValue === 'true' ? { multiple: true } : {}),
...(first !== undefined && first !== null && first !== '' ? { defaultValue: String(first) } : {}), ...(first !== undefined && first !== null && first !== '' ? { defaultValue: String(first) } : {}),
@@ -94,6 +98,7 @@ export function exportBody(params: readonly GatewayParam[], values: GatewayValue
const body: { name: string; type: string; value: string[][] }[] = []; const body: { name: string; type: string; value: string[][] }[] = [];
const missing: string[] = []; const missing: string[] = [];
for (const p of params) { for (const p of params) {
if (p.hidden) continue;
const raw = values[p.name]; const raw = values[p.name];
if (isBlank(raw)) { if (isBlank(raw)) {
if (p.required) missing.push(p.label); if (p.required) missing.push(p.label);