crystal-node-runtime

Migração manual de um relatório Crystal → Node

Este guia descreve o fluxo manual (sem Studio). Objetivo: ter uma pasta *Report com HTML Vash + controller.js, servida pela sua app Node, gerando PDF por um dos engines deste pacote.

1. Estrutura da pasta do relatório

src/core/
  lotes/
    dicon/
      termo/
        cienciaReport/
          controller.js
          report.html
          report_header.html      (opcional)
          report_footer.html      (opcional)
          crystal-pdf.json        (opcional: margens/página)
          images/                 (opcional)

Regras:

2. Instalar o runtime + dependências do engine

npm install @crystal-node/runtime

Escolha um engine e instale as deps dele na sua app:

Engine Comando / extras Links
Devbox (só o runtime; precisa de res.pdf)
Puppeteer npm install puppeteer vash https://www.npmjs.com/package/puppeteer · https://www.npmjs.com/package/vash
wkhtmltopdf npm install vash + binário no SO https://www.npmjs.com/package/vash · https://wkhtmltopdf.org/downloads.html
WeasyPrint Python 3 + pip install weasyprint (+ Pango) https://doc.courtbouillon.org/weasyprint/

Para rotas automáticas com Express:

npm install express

https://www.npmjs.com/package/express

Detalhes: engines-pdf.md.

3. Controller mínimo

Exemplo com Puppeteer (app Node genérica):

const {
  reportControllerDotnetrequest,
  crystalFunctionsRuntime,
  reportEnginePuppeteer
} = require('@crystal-node/runtime');

module.exports = async function (req, res) {
  const model = reportControllerDotnetrequest.buildReportModel(
    req,
    __dirname,
    crystalFunctionsRuntime
  );

  await reportEnginePuppeteer.renderReportPdf(model, res, {
    reportDir: __dirname,
    pdf: {
      marginTop: 28,
      marginBottom: 14,
      marginLeft: 0,
      marginRight: 0,
      pageWidth: '215.9mm',
      pageHeight: '279.4mm'
    }
  });
};

Em Devbox, troque só o engine:

const { reportEngineDevbox } = require('@crystal-node/runtime');
await reportEngineDevbox.renderReportPdf(model, res, { reportDir: __dirname, pdf: { ... } });

Com wkhtmltopdf:

const { reportEngineWkhtmltopdf } = require('@crystal-node/runtime');
await reportEngineWkhtmltopdf.renderReportPdf(model, res, { reportDir: __dirname, pdf: { ... } });

Payload comercial vs legado .NET

4. Template Vash

No topo de report.html (e header/footer, se existirem):

@{
  var crystalFunctions = (model && model.__crTemplateRuntime)
    ? model.__crTemplateRuntime
    : null;
  if (!crystalFunctions) {
    throw new Error("Crystal functions runtime nao inicializado no model.");
  }
}

Helpers frequentes: rows, joinRows, getField, toText, iif — ver README do pacote e o código de crystal-functions-runtime.

5. Registrar as rotas automaticamente

const path = require('path');
const express = require('express');
const { mountReportRoutes } = require('@crystal-node/runtime');

const app = express();
app.use(express.json({ limit: '20mb' }));

mountReportRoutes(app, {
  rootDir: path.join(__dirname, 'src/core'),
  basePath: '/',
  method: 'post'
});

Rota do exemplo: POST /lotes/dicon/termo/ciencia
API: rotas-report.md.

6. Checklist de migração manual

  1. Criar pasta nomeDoRelatorioReport no caminho desejado.
  2. Escrever / adaptar report.html (+ header/footer se necessário).
  3. Copiar imagens para images/.
  4. Escolher o engine PDF e instalar as dependências dele.
  5. Criar controller.js com controller + engine escolhido.
  6. Garantir mountReportRoutes (ou rota manual).
  7. Testar o POST com um body real do endpoint antigo.
  8. Ajustar aliases de datasource (subreportDatasourceKeys) se preciso.

7. O que este pacote não faz