diff --git a/CLAUDE.md b/CLAUDE.md index fd56c47..2ec8be9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,7 +48,7 @@ Es un ERP, no una página de publicidad: **ligera y rápida**. Sin librerías de - **Proxy `/api/sl/:recurso`**: el frontend arma `$select/$filter/...`; el backend valida y ejecuta solo recursos registrados (`apps/api/src/sl/sl-resources.ts`), con permiso de menú y cabeceras `B1S-*` de lista blanca. Nunca registrar `EmployeesInfo` (tiene `U_WEB_PASS`). `InventoryRequests` no existe: es `InventoryTransferRequests`. - **Permisos de menú** en SQLite (`SQLITE_PATH`), patrones `*`, `/ventas/*`, `/ventas/ordenes`; `ADMIN_WEB_USERS` garantiza el acceso total; no se puede quitar el último admin. - **Listas de documentos:** más recientes primero (`DocEntry desc`), 50 por página, **Estado = Abierto por defecto** (como el mini-erp; cambiarlo es un `defaultValue`), filtro de fecha **siempre rango [desde – hasta]**, multi-selección con **chips**, búsqueda numérica por igualdad. **Sin filtro base en las demás listas; las de `Invoices` filtran por subtipo exacto:** cuatro pantallas comparten el recurso y se separan por `DocumentSubType` (enum `BoDocumentSubType`: `bod_None` 0, `bod_InvoiceExempt` 1, `bod_DebitMemo` 2, `bod_Bill` 3, `bod_ExemptBill` 4, `bod_PurchaseDebitMemo` 5, `bod_ExportInvoice` 6…) y por `ReserveInvoice` (`tYES` = de reserva): Facturas (`tNO` y `eq bod_None`), Boletas (`tNO` y `eq bod_Bill`), Facturas de reserva (`tYES` y `eq bod_None`) y Boletas de reserva (`tYES` y `eq bod_Bill`); `baseFilter` para la lista y `fixed` (subtipo, y `ReserveInvoice` en las reservas) al crear. Nunca `ne`: los demás subtipos (factura exenta, nota de débito, boleta exenta, exportación) no se mezclan y tendrán su propia pantalla si se necesitan. `baseFilter` de la pantalla se aplica también a la navegación primero/anterior/siguiente/último. El PDF y los reportes se migran después vía API Gateway (otro servicio, con su propia cola y sesión). -- **Reportes Gateway** (Crystal Reports por el API Gateway de SAP B1, `apps/api/src/gateway/` y `apps/web/.../features/reports/`): menú «Reportes Gateway» con «Reportes» (lista agrupada por categoría y formulario de parámetros con el PDF en pantalla) y «Matrícula de reportes» (solo administradores: de los reportes que el gateway autoriza se eligen los que se usan, con título, categoría y cómo se pide cada parámetro). Servicio aparte del Service Layer: su sesión y su cola propias (`APIGATEWAY_*` en el `.env`; los datos salieron del `.env` del mini-erp y el certificado autofirmado va en `certs/apigateway.pem`). La matrícula vive en SQLite (`gateway_reports`) y arma el cuerpo de `ExportPDFData` (`[{name, type, value:[[…]]}]`: un valor `[[v]]`, varios `[[a],[b]]`, rango `[[desde,hasta]]`); los parámetros se piden con los mismos controles y ventanas de selección de los formularios (texto, número, fecha, lista fija, ventana de selección con un catálogo de listas: socios, artículos, almacenes, documentos por `DocEntry`…). Verificado en la base HANA: los reportes a medida (`DLN20015`, `RCRI0031`) generan PDF con valores discretos y fechas `yyyy-MM-dd`; los estándar de SAP (`RCRI0001`, `RCRI0002`, con títulos y rangos) devolvieron `(---)` con los formatos probados, el formato de rango/varios valores sigue sin confirmar. Los títulos y separadores de un reporte (`…@Title`, `…@separator`) se marcan ocultos. **Botón PDF de los documentos** (`reports/document-pdf.ts`, en la barra del formulario): cada reporte matriculado puede asociarse a pantallas de documento (`documents`, rutas de menú) con el parámetro que recibe el `DocEntry` (`documentParam`; los demás parámetros toman su valor por defecto) y marcarse «por defecto» (`defaultFor`, uno por pantalla, como en SAP: marcarlo quita la marca al anterior). «PDF» imprime el formato por defecto (o el primero) en una ventana con descarga; si hay más, el menú «Otros formatos» los lista. Sin formatos, sin permiso o sin API Gateway el botón no aparece. API: `GET /api/gateway/documents?screen=` y `POST /api/gateway/documents/export {screen, code, docEntry}`; basta el permiso de menú del documento (no el de «Reportes»). Aún no hay botón en pagos, asientos ni maestros. +- **Reportes Gateway** (Crystal Reports por el API Gateway de SAP B1, `apps/api/src/gateway/` y `apps/web/.../features/reports/`): menú «Reportes Gateway» con «Reportes» (lista agrupada por categoría y formulario de parámetros con el PDF en pantalla) y «Matrícula de reportes» (solo administradores: de los reportes que el gateway autoriza se eligen los que se usan, con título, categoría y cómo se pide cada parámetro). Servicio aparte del Service Layer: su sesión y su cola propias (`APIGATEWAY_*` en el `.env`; los datos salieron del `.env` del mini-erp y el certificado autofirmado va en `certs/apigateway.pem`). La matrícula vive en SQLite (`gateway_reports`) y arma el cuerpo de `ExportPDFData` (`[{name, type, value:[[…]]}]`: un valor `[[v]]`, varios `[[a],[b]]`, rango `[[desde,hasta]]`); los parámetros se piden con los mismos controles y ventanas de selección de los formularios (texto, número, fecha, lista fija, ventana de selección con un catálogo de listas: socios, artículos, almacenes, documentos por `DocEntry`…). Verificado en la base HANA: los reportes a medida (`DLN20015`, `RCRI0031`) generan PDF con valores discretos y fechas `yyyy-MM-dd`; los estándar de SAP (`RCRI0001`, `RCRI0002`, con títulos y rangos) devolvieron `(---)` con los formatos probados, el formato de rango/varios valores sigue sin confirmar. Los títulos y separadores de un reporte (`…@Title`, `…@separator`) se marcan ocultos. **Botón PDF de los documentos** (`reports/document-pdf.ts`, en la barra del formulario): cada reporte matriculado puede asociarse a pantallas de documento (`documents`, rutas de menú) con el parámetro que recibe el `DocEntry` (`documentParam`; los demás parámetros toman su valor por defecto) y marcarse «por defecto» (`defaultFor`, uno por pantalla, como en SAP: marcarlo quita la marca al anterior). «PDF» imprime el formato por defecto (o el primero) en una ventana con descarga; si hay más, el menú «Otros formatos» los lista. Sin formatos, sin permiso o sin API Gateway el botón no aparece. API: `GET /api/gateway/documents?screen=` y `POST /api/gateway/documents/export {screen, code, docEntry}`; basta el permiso de menú del documento (no el de «Reportes»). Aún no hay botón en pagos, asientos ni maestros. Un reporte con el parámetro `Dockey@` es un **layout de documento** de SAP: la matrícula lo propone como `documentParam`. En `SBO_TAGUMEDICA_21` el Gateway autoriza 179 reportes `RCRI…` (ninguno `DLN…`; `DLN20015` era de otra base): ahí candidatos como `RCRI0083` (GuiaRemision PT) hay que revisarlos por sus parámetros. ## Flujo de las pantallas de documento diff --git a/apps/web/src/app/features/reports/registry.page.ts b/apps/web/src/app/features/reports/registry.page.ts index 69e0890..34ae439 100644 --- a/apps/web/src/app/features/reports/registry.page.ts +++ b/apps/web/src/app/features/reports/registry.page.ts @@ -38,6 +38,9 @@ const KINDS: { value: GatewayParamKind; label: string }[] = [ ]; const yesNo = [{ value: YES, label: YES }, { value: NO, label: NO }]; +/** El parámetro `Dockey@` es el de los layouts de documento de SAP: recibe el número del documento. */ +export const dockeyParam = (params: readonly { name: string }[]): string => params.find((p) => /^dockey@/i.test(p.name))?.name ?? ''; + /** Una definición de parámetro como fila editable de la tabla. */ const toRow = (p: GatewayParam): Row => ({ name: p.name, @@ -106,7 +109,7 @@ export function toParam(r: Row): GatewayParam {

«Tipo de control» decide cómo se pide al usuario; con «Ventana de selección» se elige la lista. «Oculto» es para títulos y separadores del reporte: no se piden ni se envían. «Opciones» de una lista fija: valor=etiqueta; valor=etiqueta.

Botón PDF de los documentos

-

Marque los documentos que ofrecen este formato. El que se marca «Por defecto» es el que abre el botón «PDF» de ese documento (uno solo por documento, como en SAP); los demás salen en «Otros formatos». El número del documento (DocEntry) se envía en el parámetro elegido abajo; los demás parámetros toman su valor por defecto.

+

Un reporte con el parámetro Dockey@ es un layout de documento de SAP y se propone solo aquí. Marque los documentos que ofrecen este formato. El que se marca «Por defecto» es el que abre el botón «PDF» de ese documento (uno solo por documento, como en SAP); los demás salen en «Otros formatos». El número del documento (DocEntry) se envía en el parámetro elegido abajo; los demás parámetros toman su valor por defecto.

@@ -126,7 +129,7 @@ export function toParam(r: Row): GatewayParam {
DocumentoOfrecerPor defecto
- + @for (r of rows(); track r.code) { @@ -135,6 +138,7 @@ export function toParam(r: Row): GatewayParam { + } @empty { - + }
CódigoTítuloCategoríaParámetrosEstado
CódigoTítuloCategoríaParámetrosDocumentosEstado
{{ r.title }} {{ r.category }} {{ r.params.length }}{{ r.documents?.length ? r.documents!.length + ' documento(s)' : '' }} {{ r.enabled ? 'Habilitado' : 'Deshabilitado' }} @@ -147,7 +151,7 @@ export function toParam(r: Row): GatewayParam {
{{ registry.isLoading() ? 'Cargando…' : 'Aún no hay reportes matriculados. Pulse «Matricular reporte».' }}
{{ registry.isLoading() ? 'Cargando…' : 'Aún no hay reportes matriculados. Pulse «Matricular reporte».' }}
@@ -271,7 +275,7 @@ export class RegistryPage { this.message.set({ kind: 'info', text: `Cargando los parámetros de ${chosen.code}…` }); try { const params = await this.api.parameters(chosen.code); - this.draft.set({ code: chosen.code, isNew: true, title: chosen.name, category: 'Otros', description: '', enabled: true, params: params.map(toRow), documents: [], documentParam: '', defaultFor: [] }); + this.draft.set({ code: chosen.code, isNew: true, title: chosen.name, category: 'Otros', description: '', enabled: true, params: params.map(toRow), documents: [], documentParam: dockeyParam(params), defaultFor: [] }); this.message.set(null); } catch (e) { this.message.set({ kind: 'err', text: apiErrorMessage(e) }); @@ -281,7 +285,7 @@ export class RegistryPage { // --- editar y guardar ---------------------------------------------------------------------------- protected edit(r: GatewayReport): void { - this.draft.set({ code: r.code, isNew: false, title: r.title, category: r.category, description: r.description ?? '', enabled: r.enabled, params: r.params.map(toRow), documents: r.documents ?? [], documentParam: r.documentParam ?? '', defaultFor: r.defaultFor ?? [] }); + this.draft.set({ code: r.code, isNew: false, title: r.title, category: r.category, description: r.description ?? '', enabled: r.enabled, params: r.params.map(toRow), documents: r.documents ?? [], documentParam: r.documentParam ?? dockeyParam(r.params), defaultFor: r.defaultFor ?? [] }); this.message.set(null); } diff --git a/apps/web/src/app/features/reports/reports.spec.ts b/apps/web/src/app/features/reports/reports.spec.ts index 70e04c6..2c47c7a 100644 --- a/apps/web/src/app/features/reports/reports.spec.ts +++ b/apps/web/src/app/features/reports/reports.spec.ts @@ -201,3 +201,11 @@ describe('toParam', () => { expect(toParam({ name: 'Q', kind: 'lookup', lookup: 'articulos' })).not.toHaveProperty('options'); }); }); + +describe('layouts de documento', () => { + it('el parámetro Dockey@ se propone como el que recibe el número del documento', async () => { + const { dockeyParam } = await import('./registry.page'); + expect(dockeyParam([{ name: 'Title@Title' }, { name: 'Dockey@' }])).toBe('Dockey@'); + expect(dockeyParam([{ name: 'Item@Select * from OITM' }])).toBe(''); + }); +});