{"_id":"@core-tecnologias-empresariales/core-export","name":"@core-tecnologias-empresariales/core-export","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@core-tecnologias-empresariales/core-export","version":"0.1.0","description":"Exportación, reportería y visualización de datos (Excel, PDF, CSV, XML) para el ecosistema Core Tecnología Empresarial.","license":"UNLICENSED","publishConfig":{"access":"public"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":"./dist/index.js"},"dependencies":{"exceljs":"^4.4.0","pdfmake":"^0.2.15","@core-tecnologias-empresariales/core-i18n":"0.1.0","@core-tecnologias-empresariales/core-permissions":"0.1.0","@core-tecnologias-empresariales/core-utils":"0.1.0"},"devDependencies":{"typescript":"^5.7.0","@types/node":"^20.14.0","@types/pdfmake":"^0.2.10"},"scripts":{"build":"tsc -p tsconfig.json","test":"tsc -p tsconfig.json && node --test dist"},"_id":"@core-tecnologias-empresariales/core-export@0.1.0","_integrity":"sha512-d5UF5vxQpvD4/PnzZ6ASSojOo8Y7jst9hg/sc1OW6DKyS1meL4FmRPKnsLUk6l0790YbebmIRYvO82qfNZ/QmQ==","_resolved":"C:\\Users\\Administrador\\AppData\\Local\\Temp\\b55e941a59e97166c8f6772956027f21\\core-tecnologias-empresariales-core-export-0.1.0.tgz","_from":"file:core-tecnologias-empresariales-core-export-0.1.0.tgz","_nodeVersion":"20.20.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-d5UF5vxQpvD4/PnzZ6ASSojOo8Y7jst9hg/sc1OW6DKyS1meL4FmRPKnsLUk6l0790YbebmIRYvO82qfNZ/QmQ==","shasum":"14f300c2986e68373166dd1af3ba1a0ddb6d1ce4","tarball":"https://registry.npmjs.org/@core-tecnologias-empresariales/core-export/-/core-export-0.1.0.tgz","fileCount":44,"unpackedSize":119000,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH4YN5V/tj4fG9m71VHvKt6bvGwrvjPIyXwzVQqRP/YEAiB5t3P7HmFZehG8U6pG2e4sOs18WTUkDbjR1XYzTbyYmA=="}]},"_npmUser":{"name":"balamchac","email":"marcelosazop@gmail.com"},"directories":{},"maintainers":[{"name":"balamchac","email":"marcelosazop@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/core-export_0.1.0_1788053549458_0.0064316683250829065"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-30T01:32:29.302Z","0.1.0":"2026-08-30T01:32:29.603Z","modified":"2026-08-30T01:32:29.800Z"},"maintainers":[{"name":"balamchac","email":"marcelosazop@gmail.com"}],"description":"Exportación, reportería y visualización de datos (Excel, PDF, CSV, XML) para el ecosistema Core Tecnología Empresarial.","license":"UNLICENSED","readme":"# @core-tecnologias-empresariales/core-export\n\nExportación, reportería y visualización de datos del ecosistema Core Tecnología Empresarial. **No pertenece al dominio de negocio de ningún producto**: recibe datos más una definición declarativa de reporte, y produce el formato solicitado.\n\n```text\nProducto → Core Export → Excel · PDF · CSV · XML\n```\n\nNunca al revés: `core-export` no importa CorePyme, Core Contador ni Core Tributario.\n\n## La regla fundamental\n\n> **Lo que el usuario no puede consultar, no puede exportarlo.**\n\nEstá implementada, no solo documentada:\n\n- `exportReport()` valida el contexto **antes** de generar nada. Sin `tenantId` o sin `userId` lanza `ExportContextError` — una exportación sin tenant resuelto es un bug de seguridad, no un caso por defecto.\n- Un reporte con `requiredPermission` exige ese permiso, evaluado con `core-permissions`. **Falla cerrado**: contexto sin permisos = sin exportación.\n- Las columnas con `requiredPermission` se **omiten del archivo** cuando el usuario no las tiene. Hay tests que verifican que ni la cabecera ni el dato restringido se filtran al CSV ni al XML.\n\n## Uso\n\n```ts\nimport { exportReport } from \"@core-tecnologias-empresariales/core-export\";\n\nconst definition = {\n  id: \"sales\",\n  name: \"Reporte de Ventas\",\n  requiredPermission: \"sales.export\",\n  formats: [\"csv\", \"xlsx\"],\n  columns: [\n    { key: \"fecha\", header: \"Fecha\", format: \"date\" },\n    { key: \"cliente\", header: \"Cliente\" },\n    { key: \"total\", header: \"Total\", format: \"currency\" },\n    { key: \"margen\", header: \"Margen\", requiredPermission: \"sales.margin.read\" },\n  ],\n};\n\nconst result = await exportReport({\n  definition,\n  data: ventasYaFiltradas,   // el producto entrega los datos; core-export no consulta ninguna DB\n  format: \"csv\",\n  context: { tenantId, userId, permissions: permisosDelUsuario },\n});\n\n// result: { id, format, status, fileName, size, content, mimeType, rowCount }\n```\n\nLos datos llegan **ya filtrados** por el producto: si el usuario aplicó filtros en pantalla, el archivo refleja esa selección. Core Export no consulta la base de datos de nadie.\n\n`result.rowCount` y `result.id` están pensados para alimentar `core-audit` cuando el producto requiera trazabilidad.\n\n## Motores en v0.1\n\n### CSV — sin dependencias\n\nEscapado RFC 4180 (comillas dobladas, entrecomillado ante delimitador/comillas/saltos de línea), CRLF, delimitador configurable.\n\n**BOM UTF-8 por defecto:** sin él, Excel abre el archivo con la codificación del sistema y los acentos salen corruptos. La spec exige compatibilidad con Excel de fábrica, así que el default lo garantiza; `{ bom: false }` lo desactiva para consumidores programáticos.\n\nLas fechas se serializan en ISO para no depender del locale de quien abra el archivo.\n\n### XML — genérico, sin dependencias\n\nNodos anidados, atributos, namespaces con y sin prefijo, escapado de texto y de atributos, self-closing, salida compacta o indentada.\n\n**La lógica tributaria no vive acá.** Core Tributario usa este motor para materializar sus DTE, pero conserva el dominio:\n\n```ts\ngenerateXml({\n  name: \"DTE\",\n  namespaces: { \"\": \"http://www.sii.cl/SiiDte\" },\n  children: [...],  // la estructura la define Core Tributario, no core-export\n});\n```\n\nUn atributo `undefined` se omite en vez de emitirse vacío — distinción que importa al validar contra XSD.\n\n### PDF — pdfmake\n\nElegido porque resuelve declarativamente lo que un reporte necesita, y sobre todo porque **repite el encabezado de la tabla en cada página** (`headerRows: 1`) — con un motor de bajo nivel esa paginación hay que programarla a mano.\n\n```ts\nconst result = await exportReport({\n  definition,\n  data,\n  format: \"pdf\",\n  context,\n  pdf: {\n    logo: logoDataUri,                    // o Buffer\n    organizationName: \"Empresa Ejemplo SpA\",\n    taxId: \"76.543.210-K\",\n    period: \"Agosto 2026\",\n    metrics: [\n      { label: \"Ventas\", value: \"$12.500.000\" },\n      { label: \"Documentos\", value: \"482\" },\n      { label: \"IVA\", value: \"$2.375.000\" },\n    ],\n    orientation: \"landscape\",\n    locale: \"es-CL\",\n    timeZone: \"America/Santiago\",\n  },\n});\n```\n\nProduce:\n\n```text\n┌──────────────────────────────────────────────┐\n│ Reporte de Ventas                      [LOGO]│\n│ Empresa Ejemplo SpA · 76.543.210-K           │\n│ Período: Agosto 2026                         │\n│ Filtros aplicados: Sucursal: Casa Matriz     │\n├──────────────────────────────────────────────┤\n│ Indicadores                                  │\n│ Ventas        Documentos       IVA           │\n│ $12.500.000   482              $2.375.000    │\n├──────────────────────────────────────────────┤\n│ Detalle                                      │\n│ Fecha | Cliente | Total      ← se repite     │\n│ ...                            en cada página│\n├──────────────────────────────────────────────┤\n│ Generado el 29-08-2026    Página 1 de 8      │\n└──────────────────────────────────────────────┘\n```\n\nLos filtros del reporte se muestran automáticamente (los que tengan `label`). Las columnas `number`/`currency`/`percent` se alinean a la derecha.\n\n**Fuentes:** por defecto Helvetica, una de las 14 tipografías que el propio formato PDF incluye — no requiere archivos ni empaquetar fuentes. Para cumplir la identidad visual, el producto pasa las rutas de los `.ttf` de IBM Plex Sans vía `pdf.fonts`, mismo criterio que `core-ui`.\n\n`buildDocDefinition()` se exporta aparte: es una función pura, así que la estructura del reporte se puede testear sin parsear un PDF binario.\n\n### Excel — ExcelJS\n\n```ts\nconst result = await exportReport({\n  definition,\n  data,\n  format: \"xlsx\",\n  context,\n  xlsx: {\n    summary: {\n      title: \"Reporte de Ventas\",\n      metadata: [{ label: \"Período\", value: \"Agosto 2026\" }],\n      aggregations: [\n        { label: \"Total ventas\", column: \"total\", fn: \"SUM\" },\n        { label: \"Documentos\", column: \"cliente\", fn: \"COUNT\" },\n      ],\n    },\n    orientation: \"landscape\",\n  },\n});\n```\n\nProduce dos hojas: `Resumen` con **fórmulas que apuntan a `Detalle`** (`=SUM(Detalle!C2:C3)`), nunca valores duplicados — exactamente el patrón `Resumen/Detalle/Totales` de la spec. Encabezado congelado y autofiltro activados por defecto, formatos numéricos por tipo de columna (moneda, porcentaje, fecha), y orientación de impresión configurable.\n\nUna agregación sobre una columna que el usuario no puede ver (filtrada por `visibleColumns`) se omite silenciosamente en vez de romper el archivo.\n\n`sheetRange()` y `columnLetter()` se exportan aparte para construir fórmulas propias (`sheetRange(\"Ventas\", 2, 2, 101)` → `\"Ventas!C2:C101\"`), con soporte para nombres de hoja con espacios o apóstrofes.\n\n## Aún no implementado\n\nGráficos (en pantalla van a `core-dashboard`; insertados en Excel/PDF quedan para cuando ese paquete exista), exportaciones asíncronas vía `core-jobs`, y el menú de exportación en `core-ui`.\n\n> **Decisión pendiente heredada de `CLAUDE.md` §22:** el flujo asíncrono necesita almacenamiento temporal para el archivo generado, y `core-storage` fue descartado del catálogo. No bloquea el MVP (descarga directa), sí la segunda etapa.\n\n## Instalación\n\n```bash\npnpm add @core-tecnologias-empresariales/core-export\n```\n\n## Desarrollo\n\n```bash\npnpm build\npnpm test\n```\n","readmeFilename":"README.md","_rev":"1-1644dca968398c221d862b3030f9a3ea"}