initial
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: vitruvio-adicionar-menu
|
||||
description: >
|
||||
Use ONLY when the user explicitly asks to add a menu item or menu entry in a Vitruvio repository.
|
||||
Triggers: "add menu item", "add to menu", "adicionar ao menu", "criar item de menu",
|
||||
"add panel to menu", "adicionar painel no menu", or any explicit request to register
|
||||
something in the vitruvio.json "menu" array.
|
||||
Do NOT trigger for general panel/process/script creation — menu entries are separate.
|
||||
---
|
||||
|
||||
# Add Vitruvio Menu Item
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are adding an entry to the `menu` array in `vitruvio.json`. Menu entries are **never created automatically** — only when the user explicitly requests it. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Show the current menu structure
|
||||
|
||||
Read `vitruvio.json` and print the menu tree as a readable outline. Example format:
|
||||
|
||||
```
|
||||
menu:
|
||||
[0] menu-root "Meu Módulo" (MENU)
|
||||
[0] menu-panel "Meu Painel" (PAINEL → my-panel)
|
||||
[1] menu-report "Meu Relatório" (RELATORIO → my-report)
|
||||
```
|
||||
|
||||
If the `menu` array is empty or absent, say so.
|
||||
|
||||
## Step 3 — Collect item details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
- **Type** — one of:
|
||||
- `PAINEL` — links to a panel (`panelKey`)
|
||||
- `RELATORIO` — links to a report (`reportKey`)
|
||||
- `PROCESSO` — links to a process (`processKey`)
|
||||
- `MENU` — a submenu/folder (no artifact link, has `children`)
|
||||
- **Target artifact key** — the `key` of the panel/report/process to link (skip if type is `MENU`)
|
||||
- **Name** — the label shown in the menu
|
||||
- **Key** — unique key for this menu entry (suggest `menu-<artifactKey>` as default)
|
||||
- **Parent** — root level, or inside which existing `MENU` item? Show the options from Step 2.
|
||||
- **Order** — integer position within its parent (suggest next available based on existing siblings)
|
||||
- **Icon** — only relevant for root-level `MENU` items. Vitruvio uses FontAwesome numeric codes (e.g. `61441` = fa-adjust). For children, always `null`.
|
||||
|
||||
## Step 4 — Build the JSON entry
|
||||
|
||||
### Type: PAINEL
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"icon": null,
|
||||
"order": <order>,
|
||||
"type": "PAINEL",
|
||||
"panelKey": "<panelKey>",
|
||||
"children": []
|
||||
}
|
||||
```
|
||||
|
||||
### Type: RELATORIO
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"icon": null,
|
||||
"order": <order>,
|
||||
"type": "RELATORIO",
|
||||
"reportKey": "<reportKey>",
|
||||
"children": []
|
||||
}
|
||||
```
|
||||
|
||||
### Type: PROCESSO
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"icon": null,
|
||||
"order": <order>,
|
||||
"type": "PROCESSO",
|
||||
"processKey": "<processKey>",
|
||||
"children": []
|
||||
}
|
||||
```
|
||||
|
||||
### Type: MENU (submenu / root folder)
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"icon": <iconCode or null>,
|
||||
"order": <order>,
|
||||
"type": "MENU",
|
||||
"children": []
|
||||
}
|
||||
```
|
||||
|
||||
## Step 5 — Insert into vitruvio.json
|
||||
|
||||
- Read `vitruvio.json`.
|
||||
- If `menu` array does not exist, create it as an empty array first.
|
||||
- If the user chose **root level**: append the entry to the top-level `menu` array.
|
||||
- If the user chose a **parent item**: find the parent entry by key inside `menu` (search recursively if needed) and append to its `children` array.
|
||||
- Check that the chosen `key` is not already used anywhere in the menu tree before inserting.
|
||||
- Write the updated file back preserving formatting (2-space indent).
|
||||
|
||||
## Step 6 — Report
|
||||
|
||||
Tell the user:
|
||||
- Added `<type>` entry `<key>` ("Name") at `<location>` with order `<order>`
|
||||
- Remind them: menu order is relative within siblings — reorder adjacent items if needed
|
||||
- Remind them: `icon` values are FontAwesome numeric codes; use `null` for leaf items
|
||||
@@ -0,0 +1,100 @@
|
||||
# Create Vitruvio Library
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating a new static file library inside a Vitruvio repository. Libraries are static file bundles (JS, CSS, images) served by the platform as HTTP resources. They are different from scripts — scripts run server-side on Rhino; libraries are served to clients as-is. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect library details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing from their original request):
|
||||
|
||||
- **Key (sigla)** — kebab-case unique identifier. Used to reference the library and build its endpoint URL. Stable — changing it breaks any code that references it.
|
||||
- **Name** — human-readable label shown in the Vitruvio UI.
|
||||
- **Description** — one sentence about what this library provides (optional).
|
||||
- **Auth mode** — one of:
|
||||
- `PUBLIC` — accessible without authentication (default; use for most JS/CSS bundles)
|
||||
- `STATIC_TOKEN` — token required; use when files must not be publicly accessible
|
||||
- **Mobile enabled?** — `true` if mobile clients need to load these files; `false` otherwise (default: `false`).
|
||||
- **What files will this library contain?** — brief description so you can create a useful starting placeholder (e.g. "custom JS utilities for panels", "CSS theme overrides", "image assets").
|
||||
|
||||
## Step 3 — Create the directory and placeholder file
|
||||
|
||||
```bash
|
||||
mkdir -p libraries/<key>
|
||||
```
|
||||
|
||||
Create a placeholder file appropriate to what the user described:
|
||||
|
||||
- For a **JS library**: `libraries/<key>/<key>.js`
|
||||
- For a **CSS library**: `libraries/<key>/<key>.css`
|
||||
- For an **image/mixed library**: `libraries/<key>/README.md` explaining what belongs here
|
||||
|
||||
### JS placeholder
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Library: <name>
|
||||
* Key: <key>
|
||||
* Description: <description>
|
||||
*
|
||||
* These files are served as static HTTP resources.
|
||||
* Access URL: vBibliotecaService.buildEndpointUrl('<key>', '<key>.js')
|
||||
*/
|
||||
|
||||
// Add your client-side JavaScript here.
|
||||
// This runs in the browser — full ES6+ is supported (unlike server-side Rhino scripts).
|
||||
```
|
||||
|
||||
### CSS placeholder
|
||||
|
||||
```css
|
||||
/**
|
||||
* Library: <name>
|
||||
* Key: <key>
|
||||
* Description: <description>
|
||||
*
|
||||
* These files are served as static HTTP resources.
|
||||
* Access URL: vBibliotecaService.buildEndpointUrl('<key>', '<key>.css')
|
||||
*/
|
||||
|
||||
/* Add your styles here */
|
||||
```
|
||||
|
||||
## Step 4 — Register in vitruvio.json
|
||||
|
||||
Read `vitruvio.json`, find or create the `"libraries"` array, and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"type": "LOCAL",
|
||||
"authMode": "<authMode>",
|
||||
"authToken": null,
|
||||
"mobileEnabled": <mobileEnabled>,
|
||||
"files": "libraries/<key>/"
|
||||
}
|
||||
```
|
||||
|
||||
Omit `"description"` if not provided. Preserve the existing file structure and all other entries.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- Directory created: `libraries/<key>/`
|
||||
- Placeholder file(s) created
|
||||
- Registered in `vitruvio.json` with key `<key>`
|
||||
- How to get the serving URL at runtime:
|
||||
```javascript
|
||||
var url = vBibliotecaService.buildEndpointUrl('<key>', 'filename.js');
|
||||
```
|
||||
- Remind them: files in this directory are served as-is — client-side JS here can use modern ES6+, unlike server-side Rhino scripts
|
||||
@@ -0,0 +1,109 @@
|
||||
# Create Vitruvio Endpoint
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating a new REST endpoint inside a Vitruvio repository. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect endpoint details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing from their original request):
|
||||
|
||||
- **Key** — kebab-case unique identifier. Forms the URL path. Changing it later breaks external integrations.
|
||||
- **Name** — human-readable label shown in the Vitruvio UI.
|
||||
- **Description** — one sentence about what this endpoint does.
|
||||
- **HTTP verbs** — which methods to implement: GET, POST, PUT, PATCH, DELETE. Only scaffold the ones actually needed.
|
||||
- **Auth mode** — one of:
|
||||
- `PUBLIC` — no authentication (default)
|
||||
- `STATIC_TOKEN` — token in query param `_x_token_auth` or header `X-WS-TOKEN-AUTH`
|
||||
- `VITRUVIO_WS_USER_AUTH` — Vitruvio bearer token; `loggedUser` is available in the script
|
||||
- `HTTP_BASIC_AUTH` — HTTP basic auth; `loggedUser` is available; roles validated by Vitruvio admin
|
||||
|
||||
## Step 3 — Create the file
|
||||
|
||||
Target path: `endpoints/<key>.js`
|
||||
|
||||
URL after deploy:
|
||||
|
||||
| authMode | URL |
|
||||
|---|---|
|
||||
| `PUBLIC` | `/api/integration/public/<key>` |
|
||||
| `STATIC_TOKEN` | `/api/integration/tokenauth/<key>` |
|
||||
| `VITRUVIO_WS_USER_AUTH` | `/api/integration/bearerauth/<key>` |
|
||||
| `HTTP_BASIC_AUTH` | `/api/integration/bauth/<key>` |
|
||||
|
||||
Template (include only the requested verbs):
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Nome: <name>
|
||||
* Sigla: <key>
|
||||
* Descrição: <description>
|
||||
* Auth: <authMode>
|
||||
*/
|
||||
function WebService() {
|
||||
|
||||
// this.onGet = function(params) { ... } ← GET / DELETE: params has .headers and .query
|
||||
// this.onPost = function(params) { ... } ← POST / PUT / PATCH: params also has .requestBody (string, always JSON.parse before use)
|
||||
|
||||
this.onPost = function(params) {
|
||||
try {
|
||||
var body = JSON.parse(params.requestBody);
|
||||
if (!body.id) throw 'Missing required field: id';
|
||||
|
||||
// implementation here
|
||||
|
||||
return JSON.stringify({ success: true });
|
||||
} catch (e) {
|
||||
return JSON.stringify({ error: e.toString() });
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = new WebService();
|
||||
```
|
||||
|
||||
Rules (Rhino ES5 — no exceptions):
|
||||
- No `let`, `const`, arrow functions, template literals, destructuring, spread, `class`, or `import/export`
|
||||
- Use `var` everywhere
|
||||
- Always `JSON.parse(params.requestBody)` before accessing the body — never trust it raw
|
||||
- Always return strings — `JSON.stringify(obj)`, not raw objects
|
||||
- Return `null` or nothing for `204 No Content`; return a string for `200 OK`
|
||||
- Remove unused verb stubs entirely — don't leave placeholder bodies
|
||||
- Never concatenate user input into SQL strings; use named bind params (`:paramName`)
|
||||
- Don't hardcode datasource names or tokens — read from `vConfigService` or a DB config table
|
||||
- Put heavy logic in a separate script loaded via `libService.loadScript`, not inline in the endpoint
|
||||
|
||||
## Step 4 — Register in vitruvio.json
|
||||
|
||||
Read `vitruvio.json`, find or create the `"endpoints"` array, and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"language": "javascript",
|
||||
"authMode": "<authMode>",
|
||||
"active": true,
|
||||
"source": "endpoints/<key>.js"
|
||||
}
|
||||
```
|
||||
|
||||
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created: `endpoints/<key>.js`
|
||||
- Registered in `vitruvio.json` with key `<key>`
|
||||
- URL once deployed (based on authMode)
|
||||
- Which verbs were scaffolded
|
||||
@@ -0,0 +1,279 @@
|
||||
# Create Vitruvio Desktop Form
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
Desktop forms are Vaadin 8 forms defined in XML and rendered by the Vitruvio engine. This
|
||||
skill creates the **desktop** form. There are two variants that share almost all of their
|
||||
component vocabulary but differ in their root element and how variables flow — see
|
||||
**Differences: panel vs process** below.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Determine variant and target
|
||||
|
||||
Ask (only what is missing):
|
||||
|
||||
- **Panel or process form?** (decides root element / file location — see below)
|
||||
- **Key** — the panel or process key (the folder name).
|
||||
- For a **process**, which `formKey`(s) are needed — they must match the `activiti:formKey`
|
||||
values in `processes/<key>/<key>.bpmn`.
|
||||
- **What should the form show/do?** — fields and behaviour, so you can scaffold something useful.
|
||||
|
||||
## Differences: panel vs process
|
||||
|
||||
| | Panel | Process |
|
||||
|----------------|-------|---------|
|
||||
| File | `panels/<key>/<key>-desktop.xml` | `processes/<key>/<key>-desktop.xml` |
|
||||
| Root element | `<panel-form>` | `<forms>` (with `processKey` attribute optional) |
|
||||
| Namespace | `http://www.davinti.com.br/vitruvio/form/panel` | `http://www.davinti.com.br/vitruvio/form` |
|
||||
| XSD | `vitruvio-panel-form.xsd` | `vitruvio-form.xsd` |
|
||||
| Forms per file | exactly one `<form>` | **one `<form formKey>` per BPMN `activiti:formKey`** |
|
||||
| Variables | none built-in; use `engine.getGlobalVariable` | process variables: `engine.getVariable`/`setVariable`; submitted field `id="X"` in `formKey="A"` → variable `A_X` |
|
||||
| `<library>` | not used | optional: shared `<complex-component id>` reused via `<component-ref refId>` |
|
||||
|
||||
Everything below (components, DBTable, engine API, ES5 rules) is **identical** for both.
|
||||
|
||||
## Step 3 — Scaffold the file
|
||||
|
||||
### Panel variant — `panels/<key>/<key>-desktop.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/panel"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/panel https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-panel-form.xsd">
|
||||
|
||||
<form formKey="<key>" width="100%" height="100%">
|
||||
<name><name></name>
|
||||
<description><description></description>
|
||||
|
||||
<initScript language="JavaScript">
|
||||
<![CDATA[
|
||||
function run() {
|
||||
// called once when the panel opens
|
||||
}
|
||||
]]>
|
||||
</initScript>
|
||||
|
||||
<components>
|
||||
<VerticalLayout spacing="true" margin="true" width="100%" height="100%">
|
||||
<!-- add widgets here -->
|
||||
</VerticalLayout>
|
||||
</components>
|
||||
</form>
|
||||
</panel-form>
|
||||
```
|
||||
|
||||
### Process variant — `processes/<key>/<key>-desktop.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<forms xmlns="http://www.davinti.com.br/vitruvio/form"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-form.xsd">
|
||||
|
||||
<!-- Optional: shared components used across multiple forms -->
|
||||
<library>
|
||||
<!-- <complex-component id="libShared">...</complex-component> -->
|
||||
</library>
|
||||
|
||||
<!-- One <form> per activiti:formKey in the BPMN -->
|
||||
<form formKey="formAbertura" width="100%">
|
||||
<name>Abertura</name>
|
||||
<description>Abertura do processo</description>
|
||||
<initScript language="JavaScript">
|
||||
<![CDATA[
|
||||
function run() {
|
||||
// called when the form opens
|
||||
}
|
||||
]]>
|
||||
</initScript>
|
||||
<components>
|
||||
<VerticalLayout spacing="true" margin="true" width="100%">
|
||||
<TextField id="descricao" type="string" caption="Descrição" width="100%" required="true" />
|
||||
</VerticalLayout>
|
||||
</components>
|
||||
</form>
|
||||
|
||||
<form formKey="formExecutar" width="100%">
|
||||
<name>Executar</name>
|
||||
<description>Etapa de execução</description>
|
||||
<initScript language="JavaScript">
|
||||
<![CDATA[
|
||||
function run() {
|
||||
// read a process variable set during a previous step
|
||||
// var valor = engine.getVariable('formAbertura_descricao');
|
||||
}
|
||||
]]>
|
||||
</initScript>
|
||||
<components>
|
||||
<VerticalLayout spacing="true" margin="true" width="100%">
|
||||
<TextField id="resultado" type="string" caption="Resultado" width="100%" required="true" />
|
||||
<ComboBox id="aprovado" type="string" caption="Aprovado?" required="true" allowNullSelection="false">
|
||||
<entry key="1" value="Sim"/>
|
||||
<entry key="0" value="Não"/>
|
||||
</ComboBox>
|
||||
</VerticalLayout>
|
||||
</components>
|
||||
</form>
|
||||
|
||||
</forms>
|
||||
```
|
||||
|
||||
## Components (78 desktop components)
|
||||
|
||||
Before using a component you are unsure about, read its reference:
|
||||
`~/.local/share/vitruvio-platform/docs/components/desktop/<ComponentName>.md`
|
||||
(full list in `docs/components/INDEX.md`).
|
||||
|
||||
### Layout
|
||||
|
||||
| Component | Common attributes |
|
||||
|---|---|
|
||||
| `VerticalLayout` | `spacing`, `margin`, `width`, `height`, `expandRatio` |
|
||||
| `HorizontalLayout` | same as above |
|
||||
| `Panel` | `id`, `caption`, `width`, `height`, `margin` |
|
||||
| `TabLayout` | `id`, `width`, `framed` — contains `<Tab caption="...">` children |
|
||||
|
||||
### Widgets
|
||||
|
||||
| Component | Key attributes |
|
||||
|---|---|
|
||||
| `TextField` | `id`, `type` (`string`/`number`), `caption`, `width`, `required` |
|
||||
| `NumericField` | `id`, `type`, `caption`, `width`, `visible` |
|
||||
| `DateField` | `id`, `type` (`date`/`datetime`), `caption`, `resolution` (`DAY`/`MINUTE`) |
|
||||
| `ComboBox` | `id`, `type`, `caption`, `allowNullSelection` — children: `<entry key="..." value="..."/>` |
|
||||
| `Label` | `id`, `width`, `contentMode` (`HTML`/`TEXT`) — child: `<value>...</value>` |
|
||||
| `ButtonWidget` | `id`, `caption`, `style` (`GREEN`/`RED`/`DEFAULT`), `defaultIcon` — child: `<onClickScript>` |
|
||||
| `RichTextArea` | `id`, `type`, `caption`, `width`, `height` |
|
||||
| `ImageWidget` | `id`, `width`, `height` — child: `<image><base64 extension="png">...</base64></image>` |
|
||||
|
||||
### DBTable (data grid)
|
||||
|
||||
```xml
|
||||
<DBTable id="tbDados" type="string" width="100%" rows="8"
|
||||
exportXLS="true" showRowCount="true" selectable="true">
|
||||
<datasource>
|
||||
<!-- Option A: static query -->
|
||||
<freeQuery connection-key="vitruvio_producao">
|
||||
<![CDATA[
|
||||
SELECT col1, col2
|
||||
FROM my_table
|
||||
WHERE param = ${myParam}
|
||||
]]>
|
||||
</freeQuery>
|
||||
|
||||
<!-- Option B: dynamic query built in JS -->
|
||||
<sqlBuilderDataSource connection-key="vitruvio_producao" language="JavaScript">
|
||||
<![CDATA[
|
||||
function buildSQL(params) {
|
||||
var sql = 'SELECT col1, col2 FROM my_table WHERE 1=1';
|
||||
var val = engine.getField('myFilter').getValue();
|
||||
if (val) {
|
||||
sql += ' AND col1 = ${val}';
|
||||
params.put('val', val);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
]]>
|
||||
</sqlBuilderDataSource>
|
||||
</datasource>
|
||||
<key-field>CHAVE</key-field>
|
||||
<columns>
|
||||
<column name="COL1" caption="Column 1" expand-ratio="1"/>
|
||||
<column name="COL2" caption="Column 2" expand-ratio="2"/>
|
||||
<generated name="Action" expand-ratio="0.5">
|
||||
<scriptColumnGenerator language="JavaScript">
|
||||
<![CDATA[
|
||||
function Generator() {
|
||||
var com = libService.loadScript('vaadinComponents');
|
||||
this.generate = function(itemId, columnId, item, container) {
|
||||
var btn = com.buttonIcon('action', function() {
|
||||
var id = item.getItemProperty('CHAVE').getValue();
|
||||
// do something
|
||||
}, 'edit');
|
||||
return com.horizontalLayout([btn]);
|
||||
}
|
||||
}
|
||||
var script = new Generator();
|
||||
]]>
|
||||
</scriptColumnGenerator>
|
||||
</generated>
|
||||
</columns>
|
||||
<bind>
|
||||
<parameter value-type="number" defaultValue="0" parameterName="myParam" field-ref="otherTable"/>
|
||||
</bind>
|
||||
</DBTable>
|
||||
```
|
||||
|
||||
**SQL in datasources:** use `${paramName}` for substitution — NOT `:paramName`. Named
|
||||
params (`:paramName`) are only for `queries/*.sql` files.
|
||||
|
||||
## engine API
|
||||
|
||||
```javascript
|
||||
// Fields
|
||||
engine.getField('id').getValue()
|
||||
engine.getField('id').getConvertedValue() // typed value (number, date, etc.)
|
||||
engine.getField('id').setValue(value)
|
||||
engine.getField('id').setEnabled(bool)
|
||||
engine.getField('id').setVisible(bool)
|
||||
engine.getField('id').setRequired(bool)
|
||||
engine.getField('id').setCaption('new caption')
|
||||
engine.getField('id').refresh() // DBTable — re-run its query
|
||||
|
||||
// Widgets / layouts
|
||||
engine.getWidgetController('id').getButton()
|
||||
engine.getLayout('id').getSelectedTab()
|
||||
|
||||
// User
|
||||
engine.getLoggedUser().getLogin()
|
||||
engine.getLoggedUser().getNome()
|
||||
|
||||
// Global variables (survive tab changes within a session)
|
||||
engine.setGlobalVariable('key', value)
|
||||
engine.getGlobalVariable('key')
|
||||
|
||||
// Process forms only — process variables:
|
||||
engine.getVariable('varName')
|
||||
engine.setVariable('varName', value)
|
||||
engine.getProcessDefinitionId()
|
||||
engine.formKey() // current form's formKey
|
||||
engine.getFormName()
|
||||
|
||||
// Open another panel / load a library
|
||||
var vUI = libService.loadScript('vUI');
|
||||
vUI.showPanel('panelKey', { param1: value1 });
|
||||
var lib = libService.loadScript('scriptKey');
|
||||
```
|
||||
|
||||
## Script rules (Rhino ES5)
|
||||
|
||||
Desktop form scripts run on **Rhino ES5** — no `let`/`const`, arrow functions, template
|
||||
literals, destructuring, `class`, `import/export`. Use `var`, string `+` concat,
|
||||
`JSON.parse`/`JSON.stringify`, `importClass(Packages.some.java.Class)` for Java interop.
|
||||
(See the repo `CLAUDE.md` "JavaScript — ES5 / Rhino Engine" section.)
|
||||
|
||||
> Note: this ES5 rule is for **desktop**. Mobile forms use modern JS on the client — see
|
||||
> **vitruvio-criar-form-mobile**.
|
||||
|
||||
## Step 4 — Manifest
|
||||
|
||||
If run **standalone**, set `"forms"."desktop"` to the file path on the existing panel or
|
||||
process entry in `vitruvio.json`. Full entry creation is handled by **vitruvio-criar-painel**
|
||||
/ **vitruvio-criar-processo**.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created/updated and which variant (panel/process).
|
||||
- For processes: each `<form formKey>` must match an `activiti:formKey` in the BPMN, and
|
||||
submitted field `id="X"` in `formKey="A"` becomes process variable `A_X`.
|
||||
- `run()` in `<initScript>` is called every time the form opens.
|
||||
- `${paramName}` for SQL substitution in datasources; `:paramName` only in named query files.
|
||||
@@ -0,0 +1,255 @@
|
||||
# Create Vitruvio Mobile Form
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
Mobile forms are **not** desktop forms with a different skin. They use a different schema,
|
||||
a smaller component set, a different JavaScript runtime, and an explicit server↔client data
|
||||
contract. Read this whole skill before scaffolding — the mistakes here are not the desktop
|
||||
mistakes.
|
||||
|
||||
## The four things that make mobile different
|
||||
|
||||
1. **Different schema.** Root is `<mobile-forms>`, namespace
|
||||
`http://www.davinti.com.br/vitruvio/mobile-form`, XSD `vitruvio-mobile-form.xsd`.
|
||||
2. **Only 18 components** (vs 78 desktop). Do **not** assume a desktop widget exists on
|
||||
mobile. The full list:
|
||||
`CheckBox, ComboBox, GoogleMapsField, HorizontalLayout, ImageLibraryByFieldValue,
|
||||
ImageWidget, Label, MaskedField, MoneyField, NumericField, OptionGroup,
|
||||
ProgressBarWidget, RatingStars, SignaturePadField, TabLayout, TextField, Toggle,
|
||||
VerticalLayout` (plus structural `SubForm`/`ItemList`). Read
|
||||
`~/.local/share/vitruvio-platform/docs/components/mobile/<Component>.md` before using one.
|
||||
3. **Two JavaScript runtimes.**
|
||||
- **Client-side** (`initScript`, `discoveryScript`, validators, component event scripts):
|
||||
**modern React-Native JS** — arrow functions, Promises, `.then()/.catch()` are fine and
|
||||
expected. Most APIs are **async** and return Promises.
|
||||
- **Server-side** (`<ServerSide><Bridge>` `execute(...)` bodies): **Rhino ES5**, same rules
|
||||
as scripts/desktop. This is the only place with access to platform libs and services
|
||||
(`libService`, `runtimeService`, db, etc.).
|
||||
4. **Data is explicit.** Nothing is auto-injected the way desktop process variables are. Every
|
||||
piece of server data the form needs must be declared — via an `<Autoload>` variable, a
|
||||
`<QueryDataSource>` (synced to the device, works offline), or fetched on demand from a
|
||||
named `<Bridge>` with `vCommunicationService.executeOnServer(...)`.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Determine variant and gather the data contract
|
||||
|
||||
Ask (only what is missing):
|
||||
|
||||
- **Panel or process mobile form?** (see **Differences: panel vs process** below)
|
||||
- **Key** — the panel or process key (folder name).
|
||||
- For a **process**: which `formKey`(s) — they must match the `activiti:formKey` in
|
||||
`processes/<key>/<key>.bpmn`.
|
||||
- **Which variables does the form need?** Because nothing is auto-injected, ask explicitly:
|
||||
- process variables to read (each becomes an `<Autoload>` `<variable>` and/or a Bridge call)
|
||||
- reference/lookup data (each becomes a `<QueryDataSource>` backed by a `queries/*.sql`)
|
||||
- any server-only logic / libs needed (each becomes a `<Bridge>`)
|
||||
- **Offline?** Whether the data sources must be available without connectivity (affects
|
||||
`autoSyncOnInit` / `autoSyncOnDiscovery`).
|
||||
|
||||
## Differences: panel vs process
|
||||
|
||||
The schema, component set, ServerSide/Bridge mechanics and JS runtimes below are **identical**
|
||||
for both. Only these differ:
|
||||
|
||||
| | Panel mobile form | Process mobile form |
|
||||
|----------------|-------------------|---------------------|
|
||||
| File | `panels/<key>/<key>-mobile.xml` | `processes/<key>/<key>-mobile.xml` |
|
||||
| Root attribute | `<mobile-forms>` (no `processKey`) | `<mobile-forms processKey="<key>">` |
|
||||
| Forms per file | one `<form>` | **one `<form formKey>` per BPMN `activiti:formKey`** (must match) |
|
||||
| Manifest | set `forms.mobile` **and** `showInMobileList: true` on the panel entry | set `forms.mobile` (and optionally `forms.mobileAlternative`) on the process entry |
|
||||
| Variables | use `engine.getGlobalVariable` / Autoload | process variables are fetched server-side via a Bridge (`runtimeService.getVariable`) and/or declared in `<Autoload>` |
|
||||
|
||||
> Filename: name files by the **artifact key + suffix** — `<key>-mobile.xml` (and
|
||||
> `<key>-desktop.xml`, `<key>.bpmn`). This keeps every form searchable by its key instead of
|
||||
> dozens of identical `form-mobile.xml` tabs. Older content used `form-mobile.xml` /
|
||||
> `form_web_mobile.xml`; the real path is whatever `forms.mobile` points to, so legacy files
|
||||
> still work — but new scaffolds use `<key>-mobile.xml`.
|
||||
|
||||
## Step 3 — Scaffold the file
|
||||
|
||||
### Skeleton (process variant shown; for a panel drop `processKey` and use a single `<form>`)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mobile-forms processKey="<key>"
|
||||
xmlns="http://www.davinti.com.br/vitruvio/mobile-form"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/mobile-form https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-mobile-form.xsd">
|
||||
|
||||
<form formKey="formColeta">
|
||||
<name>Coleta</name>
|
||||
<description>Etapa de coleta no app</description>
|
||||
|
||||
<!-- Runs when the form opens. CLIENT-SIDE: modern JS, async APIs return Promises. -->
|
||||
<initScript language="JavaScript">
|
||||
<![CDATA[
|
||||
function run() {
|
||||
// Autoloaded variables land in the global scope:
|
||||
engine.getField('nomeLoja').setValue(engine.getGlobalVariable('nomeLoja'));
|
||||
}
|
||||
]]>
|
||||
</initScript>
|
||||
|
||||
<!-- Optional: pull server data when the task is discovered (before the user opens it). -->
|
||||
<discoveryScript language="JavaScript">
|
||||
<![CDATA[
|
||||
function run() {
|
||||
var params = { id: execution.getProcessInstanceId() };
|
||||
vCommunicationService.executeOnServer('bridgeNumeroCarga', params).then(result => {
|
||||
var n = parseInt(result, 10);
|
||||
execution.setVariable('numeroCarga', n).then(ok => {}).catch(err => {
|
||||
console.log('Erro ao setar numeroCarga localmente', err);
|
||||
});
|
||||
}).catch(err => {
|
||||
console.log('Erro ao coletar numeroCarga no server', err);
|
||||
});
|
||||
}
|
||||
]]>
|
||||
</discoveryScript>
|
||||
|
||||
<!-- Block completing the task when a rule is not met. -->
|
||||
<validators>
|
||||
<ScriptValidator execution="COMPLETE" language="JavaScript" id="validadorComplete">
|
||||
<![CDATA[
|
||||
function Validator() {
|
||||
var msg;
|
||||
this.getMessage = function() { return msg; };
|
||||
this.isValid = function() {
|
||||
if (engine.getField('confirma').getValue() == 'Sim') { return true; }
|
||||
msg = 'Confirme a execução antes de finalizar.';
|
||||
return false;
|
||||
};
|
||||
}
|
||||
var validator = new Validator();
|
||||
]]>
|
||||
</ScriptValidator>
|
||||
</validators>
|
||||
|
||||
<components>
|
||||
<VerticalLayout spacing="true" margin="true" width="100%">
|
||||
<TextField id="nomeLoja" type="string" caption="Loja" readOnly="true" />
|
||||
<OptionGroup id="confirma" type="string" caption="Executou?" required="true">
|
||||
<entry key="Sim" value="Sim" />
|
||||
<entry key="Nao" value="Não" />
|
||||
</OptionGroup>
|
||||
<SignaturePadField id="assinatura" caption="Assinatura" />
|
||||
</VerticalLayout>
|
||||
</components>
|
||||
|
||||
<!-- Explicitly declared variables auto-injected into the form's scope on load. -->
|
||||
<Autoload>
|
||||
<variables autoInjectionScope="ENGINE_GLOBAL_SCOPE" autoPersist="true">
|
||||
<variable>nomeLoja</variable>
|
||||
<variable>numeroCarga</variable>
|
||||
</variables>
|
||||
</Autoload>
|
||||
|
||||
<!-- Everything the server provides to this form. -->
|
||||
<ServerSide>
|
||||
<!-- Named queries (queries/*.sql) synced to the device for offline lookups. -->
|
||||
<DataSources>
|
||||
<QueryDataSource key="qry_produtos_carga" autoSyncOnInit="true" autoSyncOnDiscovery="true" refreshInSeconds="60" />
|
||||
</DataSources>
|
||||
|
||||
<!-- Server-side functions. Rhino ES5. Full access to libService / runtimeService / db.
|
||||
Called from the client via vCommunicationService.executeOnServer('id', params). -->
|
||||
<Bridges>
|
||||
<Bridge language="JavaScript" id="bridgeNumeroCarga">
|
||||
<![CDATA[
|
||||
function execute(params) {
|
||||
var numeroCarga = runtimeService.getVariable(params.id, 'numeroCarga');
|
||||
return numeroCarga ? numeroCarga : -1;
|
||||
}
|
||||
]]>
|
||||
</Bridge>
|
||||
</Bridges>
|
||||
</ServerSide>
|
||||
</form>
|
||||
|
||||
</mobile-forms>
|
||||
```
|
||||
|
||||
## How libs and process variables reach the mobile form
|
||||
|
||||
The mobile app cannot call `libService.loadScript(...)` or read process variables directly —
|
||||
those live on the server. The pattern is always **declare a Bridge, call it from the client**:
|
||||
|
||||
```xml
|
||||
<!-- SERVER-SIDE (Rhino ES5): a lib used to build a barcode image -->
|
||||
<Bridge language="JavaScript" id="imagemCodBarras">
|
||||
<![CDATA[
|
||||
function execute(codbarras) {
|
||||
var generator = libService.loadScript('barcode-gen');
|
||||
return ',' + generator.generateEAN13BarcodeImageAsBase64({ value: codbarras });
|
||||
}
|
||||
]]>
|
||||
</Bridge>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// CLIENT-SIDE (modern JS): call the bridge, use the Promise result
|
||||
vCommunicationService.executeOnServer('imagemCodBarras', codigoBarras).then(base64 => {
|
||||
engine.getField('codigoImagem').setValue(base64);
|
||||
}).catch(err => console.log('Erro no bridge imagemCodBarras', err));
|
||||
```
|
||||
|
||||
Rules of thumb:
|
||||
- Anything needing a **platform lib, the database, or a platform service** → put it in a
|
||||
**Bridge** (server, ES5) and call it with `vCommunicationService.executeOnServer`.
|
||||
- **Reference/lookup tables** the form reads repeatedly → a **`QueryDataSource`** backed by a
|
||||
`queries/*.sql` (works offline once synced).
|
||||
- **Process variables** the form needs → either declare them in `<Autoload>` or fetch them
|
||||
in a Bridge via `runtimeService.getVariable(processInstanceId, 'varName')` and store locally
|
||||
with `execution.setVariable(...)`.
|
||||
- Client-side `execution.getVariable(...)` / `execution.setVariable(...)` are **async** and
|
||||
return Promises — use `.then()`.
|
||||
|
||||
## List-based entry: SubForm + ItemList
|
||||
|
||||
For "add many items" screens (collect a list of rows), use a `SubForm` with an `<ItemList>`:
|
||||
|
||||
```xml
|
||||
<SubForm formKey="executarAcao">
|
||||
<name>Executar AÇÃO</name>
|
||||
<initScript language="JavaScript">
|
||||
<![CDATA[ function run(apply) { if (apply) { apply(); } } ]]>
|
||||
</initScript>
|
||||
<ItemList addItemButtonCaption="Gravar na Lista" caption="Itens">
|
||||
<property id="OBSERVACAO" caption="Observação" />
|
||||
</ItemList>
|
||||
<components>
|
||||
<VerticalLayout width="100%" spacing="true" margin="true">
|
||||
<!-- fields captured per item -->
|
||||
</VerticalLayout>
|
||||
</components>
|
||||
</SubForm>
|
||||
```
|
||||
|
||||
Validate list completeness with `engine.getSubFormListSizeByStatus(function(size){ ... }, "all")`
|
||||
inside a `COMPLETE` validator.
|
||||
|
||||
## Step 4 — Manifest
|
||||
|
||||
If run **standalone**, update the matching entry in `vitruvio.json`:
|
||||
- **Panel**: set `forms.mobile = "panels/<key>/<key>-mobile.xml"` and `showInMobileList: true`.
|
||||
- **Process**: set `forms.mobile = "processes/<key>/<key>-mobile.xml"`.
|
||||
|
||||
Full entry creation is handled by **vitruvio-criar-painel** / **vitruvio-criar-processo**.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created/updated and variant (panel/process).
|
||||
- Which variables/queries/bridges were declared, and that **only declared data is available**
|
||||
on the device — anything else must be added as an Autoload variable, QueryDataSource, or Bridge.
|
||||
- Reminder: client scripts are modern JS (Promises); Bridge bodies are server-side Rhino ES5.
|
||||
- For processes: each `<form formKey>` must match an `activiti:formKey` in the BPMN.
|
||||
- Suggest reading `docs/components/mobile/` and an example
|
||||
(`~/.local/share/vitruvio-platform/examples/processes/*/form_web_mobile.xml`) for richer screens.
|
||||
@@ -0,0 +1,92 @@
|
||||
# Create Vitruvio Panel (orchestrator)
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
A panel is one or two form files plus a manifest entry:
|
||||
|
||||
| Part | Skill that owns it |
|
||||
|------|--------------------|
|
||||
| `panels/<key>/<key>-desktop.xml` (web form) | **vitruvio-criar-form-desktop** (panel variant) |
|
||||
| `panels/<key>/<key>-mobile.xml` (app form, optional) | **vitruvio-criar-form-mobile** (panel variant) |
|
||||
|
||||
This skill collects the intent once, drives those skills, and registers the panel in
|
||||
`vitruvio.json`. Do the file work by following the referenced skills — do not re-derive
|
||||
their templates here.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect panel details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
- **Key** — PascalCase or snake_case unique identifier. Used in `engine.showPanel(key)` and
|
||||
as the folder name.
|
||||
- **Name** — human-readable label shown in the Vitruvio menu.
|
||||
- **Category** — display category, hierarchical with `/` (e.g. `"Comercial"`,
|
||||
`"Auditoria/Gondola"`).
|
||||
- **Description** — one sentence (optional).
|
||||
- **Open in new window?** — `true`/`false`. Default `true`.
|
||||
- **Show in mobile list?** — `true`/`false`. Default `false`.
|
||||
- **Needs a mobile form?** — if yes, a `<key>-mobile.xml` is created too. Mobile is a different
|
||||
schema with explicit data wiring (the mobile skill will ask for variables/lookups/libs).
|
||||
- **What should the panel do?** — fields/behaviour, so the form scaffold is useful.
|
||||
|
||||
## Step 3 — Scaffold
|
||||
|
||||
```bash
|
||||
vitruvio new panel <key> --name "<name>"
|
||||
```
|
||||
|
||||
This creates `panels/<key>/<key>-desktop.xml` and the `vitruvio.json` entry. Then replace the
|
||||
generated form using the focused skills:
|
||||
|
||||
1. **Desktop form** — follow **vitruvio-criar-form-desktop** (panel variant) to write
|
||||
`panels/<key>/<key>-desktop.xml` from the user's description.
|
||||
2. **Mobile form (if requested)** — follow **vitruvio-criar-form-mobile** (panel variant) to
|
||||
write `panels/<key>/<key>-mobile.xml`. `vitruvio new` does not create it.
|
||||
|
||||
## Step 4 — Update vitruvio.json entry
|
||||
|
||||
`vitruvio new` already added the entry. Full shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"category": "<category>",
|
||||
"displayOrder": 0,
|
||||
"showInPresentation": false,
|
||||
"openInNewWindow": true,
|
||||
"showInMobileList": false,
|
||||
"displayTimeInSeconds": 0,
|
||||
"allowedGroups": [],
|
||||
"allowedUsers": [],
|
||||
"forms": {
|
||||
"desktop": "panels/<key>/<key>-desktop.xml"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fields `vitruvio new` typically leaves at defaults that need updating:
|
||||
- `"category"` — set to the user's category (default `""`).
|
||||
- `"description"` — add if provided.
|
||||
- `"openInNewWindow"` — set to `false` for same-window.
|
||||
- `"showInMobileList"` — set to `true` for mobile visibility.
|
||||
- If a mobile form was created, add `"mobile": "panels/<key>/<key>-mobile.xml"` inside `"forms"`
|
||||
(and set `showInMobileList: true`).
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- Files created: `<key>-desktop.xml` (and `<key>-mobile.xml` if applicable).
|
||||
- Registered in `vitruvio.json` with key `<key>`.
|
||||
- `run()` in `<initScript>` is called every time the panel opens.
|
||||
- `${paramName}` for SQL substitution in datasource blocks; `:paramName` only in named query files.
|
||||
- For mobile: only explicitly declared data is available on the device — see vitruvio-criar-form-mobile.
|
||||
@@ -0,0 +1,120 @@
|
||||
# Create Vitruvio Patch
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating a new Liquibase database migration patch inside a Vitruvio repository. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Inspect existing patches to determine the next changeset ID
|
||||
|
||||
```bash
|
||||
find patches/ -name "*.xml" | xargs grep -h 'id="[0-9]' 2>/dev/null | grep -oP 'id="\K[0-9]+' | sort -n | tail -1
|
||||
```
|
||||
|
||||
The next ID = last found ID + 1. If no patches exist yet, start at 1.
|
||||
|
||||
Also check whether oracle and postgresql subdirectories already exist:
|
||||
|
||||
```bash
|
||||
ls patches/
|
||||
```
|
||||
|
||||
## Step 3 — Collect migration details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
- **What this migration does** — describe the change (e.g. "add column STATUS to table PEDIDO", "create table AUDIT_LOG", "insert config rows").
|
||||
- **Author** — Vitruvio username (e.g. `joao.felix`). Use `git config user.name` if unsure.
|
||||
- **Module key** — used in the filename (e.g. `GO`, `checklist`, `faturamento`). Default: the repo's `metadata.key` from `vitruvio.json`.
|
||||
|
||||
## Step 4 — Create the patch files
|
||||
|
||||
**Both oracle/ and postgresql/ files must always be created and kept in sync.**
|
||||
|
||||
Filename convention: `{YYYYMMDDHHmm}_{MODULE_KEY}.xml` (e.g. `202506011430_checklist.xml`). Use the current date and time.
|
||||
|
||||
```bash
|
||||
mkdir -p patches/oracle patches/postgresql
|
||||
```
|
||||
|
||||
### Absolute rules
|
||||
|
||||
- **Append-only.** Never edit or delete existing `<changeSet>` entries — modifying a checksum that Liquibase already recorded breaks deployment.
|
||||
- **Unique numeric IDs.** Each `<changeSet id="...">` must have a unique ID within the repo. Increment from the last found.
|
||||
- **Always use `<preConditions onFail="MARK_RAN">`** — every changeset must be idempotent and safe to re-run on any DB state.
|
||||
- **Oracle ≠ PostgreSQL.** Write each file for its target DB — data types, sequences, and quoting differ. Never copy-paste blindly.
|
||||
- **One logical change per changeset** — don't batch unrelated changes into a single `<changeSet>`.
|
||||
|
||||
### File skeleton
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
|
||||
|
||||
<changeSet author="<author>" id="<next-id>" objectQuotingStrategy="LEGACY">
|
||||
<preConditions onError="WARN" onFail="MARK_RAN" onSqlOutput="IGNORE">
|
||||
<!-- guard appropriate for the operation — see examples below -->
|
||||
</preConditions>
|
||||
<sql endDelimiter=";" splitStatements="true" stripComments="false">
|
||||
-- SQL for this DB dialect
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
```
|
||||
|
||||
### preConditions reference
|
||||
|
||||
| Operation | Guard to use |
|
||||
|-----------|-------------|
|
||||
| CREATE TABLE | `<not><tableExists tableName="MY_TABLE"/></not>` |
|
||||
| ADD COLUMN | `<not><columnExists tableName="MY_TABLE" columnName="MY_COL"/></not>` |
|
||||
| CREATE INDEX | `<not><indexExists indexName="IDX_NAME"/></not>` |
|
||||
| ADD CONSTRAINT / FK | `<not><foreignKeyConstraintExists foreignKeyName="FK_NAME"/></not>` |
|
||||
| INSERT row (by PK) | `<sqlCheck expectedResult="0">SELECT COUNT(*) FROM MY_TABLE WHERE ID = 1</sqlCheck>` |
|
||||
| DROP TABLE | `<tableExists tableName="MY_TABLE"/>` |
|
||||
| DROP COLUMN | `<columnExists tableName="MY_TABLE" columnName="MY_COL"/>` |
|
||||
|
||||
### Oracle vs PostgreSQL differences to watch
|
||||
|
||||
| | Oracle | PostgreSQL |
|
||||
|---|---|---|
|
||||
| Auto-increment | Separate `CREATE SEQUENCE` + trigger or `DEFAULT seq.NEXTVAL` | `SERIAL` or `GENERATED ALWAYS AS IDENTITY` |
|
||||
| String type | `VARCHAR2(n)` | `VARCHAR(n)` |
|
||||
| Boolean | `NUMBER(1)` | `BOOLEAN` |
|
||||
| Date/time | `DATE`, `TIMESTAMP` | `DATE`, `TIMESTAMP` |
|
||||
| Current timestamp | `SYSDATE` | `CURRENT_TIMESTAMP` |
|
||||
| Quoting | `LEGACY` strategy (unquoted) | Same |
|
||||
|
||||
## Step 5 — Check vitruvio.json patches registration
|
||||
|
||||
The patches directory only needs to be registered once. Check if it is already there:
|
||||
|
||||
```bash
|
||||
grep -A2 '"patches"' vitruvio.json
|
||||
```
|
||||
|
||||
If not registered, add to `vitruvio.json`:
|
||||
|
||||
```json
|
||||
"patches": "patches/"
|
||||
```
|
||||
|
||||
## Step 6 — Report
|
||||
|
||||
Tell the user:
|
||||
- Files created: `patches/oracle/<filename>.xml` and `patches/postgresql/<filename>.xml`
|
||||
- Changeset IDs used
|
||||
- Summary of what each changeset does
|
||||
- Reminder: never edit existing changesets once committed — add new ones instead
|
||||
@@ -0,0 +1,216 @@
|
||||
# Create Vitruvio Process BPMN
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating the **BPMN workflow file** of a Vitruvio process. This skill is focused
|
||||
on the `.bpmn` file only — the desktop form is handled by **vitruvio-criar-form-desktop**
|
||||
(process variant) and the mobile form by **vitruvio-criar-form-mobile** (process variant).
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect flow details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
- **Key** — snake_case or camelCase unique identifier. Becomes the BPMN process ID and the
|
||||
folder name. Must match the `key` used for the process in `vitruvio.json`.
|
||||
- **Name** — human-readable label.
|
||||
- **Who can start it?** — group key(s) allowed to open the process (e.g. `gestao`,
|
||||
`admin`). Used in `activiti:candidateStarterGroups`.
|
||||
- **Steps** — the human tasks (user tasks), and which group handles each. A simple linear
|
||||
flow is enough to start.
|
||||
- **Decisions / branches?** — any exclusive gateways with conditions.
|
||||
- **Automatic steps?** — any script tasks running between user tasks.
|
||||
|
||||
## Step 3 — Write the BPMN file: `processes/<key>/<key>.bpmn`
|
||||
|
||||
### Critical rules
|
||||
|
||||
- The `<bpmn2:process id="...">` value is the canonical process identity. The importer
|
||||
reads it from the BPMN, not from vitruvio.json. **It must match the `key`.**
|
||||
- Every node must appear in a `<bpmn2:laneSet>` / `<bpmn2:lane>` **and** in the
|
||||
`<bpmndi:BPMNDi>` section — Vitruvio renders the diagram.
|
||||
- Each `activiti:formKey` on the start event and user tasks must match a
|
||||
`<form formKey="...">` in the desktop form XML (and mobile form, if present).
|
||||
- Process variables from submitted forms are auto-named `{formKey}_{fieldId}`
|
||||
(e.g. `formAbertura_status`). Gateway conditions reference them.
|
||||
- Gateway conditions use `#{variable == 'value'}` (JUEL expression language).
|
||||
- Script tasks call `vScriptService.loadScript('scriptKey', 'javascript')`, **not**
|
||||
`libService`.
|
||||
|
||||
### Minimal skeleton (start → user task → end, single lane)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn2:definitions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
||||
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
|
||||
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
|
||||
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
|
||||
xmlns:activiti="http://activiti.org/bpmn"
|
||||
id="sample-diagram"
|
||||
targetNamespace="http://bpmn.io/schema/bpmn"
|
||||
exporter="bpmn-js (https://demo.bpmn.io)"
|
||||
exporterVersion="8.2.0"
|
||||
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
|
||||
|
||||
<bpmn2:collaboration id="Collaboration_<key>">
|
||||
<bpmn2:participant id="processo_<key>" name="<Name>" processRef="<key>" />
|
||||
</bpmn2:collaboration>
|
||||
|
||||
<bpmn2:process id="<key>" name="<Name>" isExecutable="true"
|
||||
activiti:candidateStarterGroups="<starterGroup>">
|
||||
<bpmn2:laneSet>
|
||||
<bpmn2:lane id="lane_execucao" name="Execução">
|
||||
<bpmn2:flowNodeRef>inicio</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>task_executar</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>fim</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
</bpmn2:laneSet>
|
||||
|
||||
<!-- Start event: activiti:initiator stores the login of who opened the process -->
|
||||
<bpmn2:startEvent id="inicio" name="Início"
|
||||
activiti:formKey="formAbertura"
|
||||
activiti:initiator="iniciador">
|
||||
<bpmn2:outgoing>flow_inicio_task</bpmn2:outgoing>
|
||||
</bpmn2:startEvent>
|
||||
|
||||
<!-- User task: candidateGroups controls who sees it in their inbox -->
|
||||
<bpmn2:userTask id="task_executar" name="Executar"
|
||||
activiti:formKey="formExecutar"
|
||||
activiti:candidateGroups="${vStringUtils.validateRoles(gr_executores)}">
|
||||
<bpmn2:incoming>flow_inicio_task</bpmn2:incoming>
|
||||
<bpmn2:outgoing>flow_task_fim</bpmn2:outgoing>
|
||||
</bpmn2:userTask>
|
||||
|
||||
<!-- Script task example (omit if not needed):
|
||||
<bpmn2:scriptTask id="script_processar" name="Processar" scriptFormat="javascript">
|
||||
<bpmn2:incoming>flow_task_script</bpmn2:incoming>
|
||||
<bpmn2:outgoing>flow_script_fim</bpmn2:outgoing>
|
||||
<bpmn2:script>var f = vScriptService.loadScript('meu_script', 'javascript');
|
||||
f(execution);</bpmn2:script>
|
||||
</bpmn2:scriptTask>
|
||||
-->
|
||||
|
||||
<!-- Exclusive gateway example (omit if not needed):
|
||||
<bpmn2:exclusiveGateway id="gw_decisao" name="Aprovado?">
|
||||
<bpmn2:incoming>flow_task_gw</bpmn2:incoming>
|
||||
<bpmn2:outgoing>flow_sim</bpmn2:outgoing>
|
||||
<bpmn2:outgoing>flow_nao</bpmn2:outgoing>
|
||||
</bpmn2:exclusiveGateway>
|
||||
<bpmn2:sequenceFlow id="flow_sim" name="Sim" sourceRef="gw_decisao" targetRef="fim">
|
||||
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">#{formExecutar_aprovado == '1'}</bpmn2:conditionExpression>
|
||||
</bpmn2:sequenceFlow>
|
||||
<bpmn2:sequenceFlow id="flow_nao" name="Não" sourceRef="gw_decisao" targetRef="task_executar">
|
||||
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">#{formExecutar_aprovado == '0'}</bpmn2:conditionExpression>
|
||||
</bpmn2:sequenceFlow>
|
||||
-->
|
||||
|
||||
<bpmn2:endEvent id="fim" name="Fim">
|
||||
<bpmn2:incoming>flow_task_fim</bpmn2:incoming>
|
||||
</bpmn2:endEvent>
|
||||
|
||||
<bpmn2:sequenceFlow id="flow_inicio_task" sourceRef="inicio" targetRef="task_executar" />
|
||||
<bpmn2:sequenceFlow id="flow_task_fim" sourceRef="task_executar" targetRef="fim" />
|
||||
</bpmn2:process>
|
||||
|
||||
<!-- BPMNDi: visual layout — required for the diagram to render -->
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_<key>">
|
||||
|
||||
<bpmndi:BPMNShape id="Participant_di" bpmnElement="processo_<key>" isHorizontal="true">
|
||||
<dc:Bounds x="100" y="80" width="750" height="180" />
|
||||
</bpmndi:BPMNShape>
|
||||
|
||||
<bpmndi:BPMNShape id="lane_execucao_di" bpmnElement="lane_execucao" isHorizontal="true">
|
||||
<dc:Bounds x="130" y="80" width="720" height="180" />
|
||||
</bpmndi:BPMNShape>
|
||||
|
||||
<bpmndi:BPMNShape id="inicio_di" bpmnElement="inicio">
|
||||
<dc:Bounds x="192" y="152" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="195" y="195" width="30" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
|
||||
<bpmndi:BPMNShape id="task_executar_di" bpmnElement="task_executar">
|
||||
<dc:Bounds x="310" y="130" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
|
||||
<bpmndi:BPMNShape id="fim_di" bpmnElement="fim">
|
||||
<dc:Bounds x="492" y="152" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="497" y="195" width="19" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
|
||||
<bpmndi:BPMNEdge id="flow_inicio_task_di" bpmnElement="flow_inicio_task">
|
||||
<di:waypoint x="228" y="170" />
|
||||
<di:waypoint x="310" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
|
||||
<bpmndi:BPMNEdge id="flow_task_fim_di" bpmnElement="flow_task_fim">
|
||||
<di:waypoint x="410" y="170" />
|
||||
<di:waypoint x="492" y="170" />
|
||||
</bpmndi:BPMNEdge>
|
||||
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn2:definitions>
|
||||
```
|
||||
|
||||
### Multi-lane pattern (when tasks belong to different roles)
|
||||
|
||||
Add each lane inside `<bpmn2:laneSet>`, list the node IDs inside each lane, and adjust
|
||||
the BPMNDi bounds:
|
||||
|
||||
```xml
|
||||
<bpmn2:laneSet>
|
||||
<bpmn2:lane id="lane_gestao" name="Gestão">
|
||||
<bpmn2:flowNodeRef>inicio</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>fim</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
<bpmn2:lane id="lane_execucao" name="Execução">
|
||||
<bpmn2:flowNodeRef>task_executar</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
</bpmn2:laneSet>
|
||||
```
|
||||
|
||||
### Script task — script side
|
||||
|
||||
```javascript
|
||||
// In <bpmn2:script> inside a scriptTask:
|
||||
var f = vScriptService.loadScript('meu_script', 'javascript');
|
||||
f(execution);
|
||||
|
||||
// In the script file itself (pattern: process/task script — see vitruvio-criar-script):
|
||||
(function(execution) {
|
||||
var db = libService.loadScript('db');
|
||||
var banco = new db(db.VITRUVIO_DATASOURCE);
|
||||
var status = execution.getVariable('formAbertura_status');
|
||||
// ...
|
||||
})(execution)
|
||||
```
|
||||
|
||||
## Step 4 — Manifest
|
||||
|
||||
If this skill is run **standalone** (the process already exists in `vitruvio.json`),
|
||||
ensure its entry has `"bpmn": "processes/<key>/<key>.bpmn"`. Do not create or modify
|
||||
the rest of the entry here — full registration is handled by **vitruvio-criar-processo**.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created/updated: `processes/<key>/<key>.bpmn`
|
||||
- The process identity is the `<bpmn2:process id>` — it must match the key.
|
||||
- Each `activiti:formKey` must have a matching `<form formKey="...">` in the form XML
|
||||
(create/update it with **vitruvio-criar-form-desktop** / **vitruvio-criar-form-mobile**).
|
||||
- Submitted field `id="X"` in `formKey="formAbertura"` becomes variable `formAbertura_X`.
|
||||
- For complex flows, recommend editing the BPMN in bpmn.io or Camunda Modeler before deploying.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Create Vitruvio Process (orchestrator)
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
A process is made of up to three artifacts plus a manifest entry:
|
||||
|
||||
| Part | Skill that owns it |
|
||||
|------|--------------------|
|
||||
| `processes/<key>/<key>.bpmn` (workflow) | **vitruvio-criar-processo-bpmn** |
|
||||
| `processes/<key>/<key>-desktop.xml` (web form) | **vitruvio-criar-form-desktop** (process variant) |
|
||||
| `processes/<key>/<key>-mobile.xml` (app form, optional) | **vitruvio-criar-form-mobile** (process variant) |
|
||||
|
||||
This skill collects the intent once, drives those skills, and registers the process in
|
||||
`vitruvio.json`. Do the file work by following the referenced skills — do not re-derive
|
||||
their templates here.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect process details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
- **Key** — snake_case or camelCase unique identifier. Becomes the BPMN process ID and the
|
||||
folder name.
|
||||
- **Name** — human-readable label.
|
||||
- **Description** — one sentence (optional).
|
||||
- **Who can start it?** — group key(s) for `activiti:candidateStarterGroups`.
|
||||
- **Steps** — the human tasks and which group handles each. A simple linear flow is enough.
|
||||
- **Needs a script task?** — automatic steps running a script between user tasks.
|
||||
- **Needs a mobile form?** — if yes, a `<key>-mobile.xml` is created too. Remember mobile is a
|
||||
different schema with explicit data wiring — variables, lookups and libs must be named
|
||||
up front (the mobile skill will ask).
|
||||
|
||||
## Step 3 — Scaffold
|
||||
|
||||
```bash
|
||||
vitruvio new process <key> --name "<name>"
|
||||
```
|
||||
|
||||
This creates `processes/<key>/<key>.bpmn`, `processes/<key>/<key>-desktop.xml`, and the
|
||||
`vitruvio.json` entry. Then replace the generated files using the focused skills:
|
||||
|
||||
1. **BPMN** — follow **vitruvio-criar-processo-bpmn** to write `processes/<key>/<key>.bpmn`
|
||||
from the user's steps/branches.
|
||||
2. **Desktop form** — follow **vitruvio-criar-form-desktop** (process variant) to write
|
||||
`processes/<key>/<key>-desktop.xml`, one `<form formKey>` per `activiti:formKey` in the BPMN.
|
||||
3. **Mobile form (if requested)** — follow **vitruvio-criar-form-mobile** (process variant) to
|
||||
write `processes/<key>/<key>-mobile.xml`. `vitruvio new` does not create it.
|
||||
|
||||
## Step 4 — Update vitruvio.json entry
|
||||
|
||||
`vitruvio new` already added the entry. Full shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"bpmn": "processes/<key>/<key>.bpmn",
|
||||
"forms": {
|
||||
"desktop": "processes/<key>/<key>-desktop.xml"
|
||||
},
|
||||
"schedules": []
|
||||
}
|
||||
```
|
||||
|
||||
- Add `"description"` if the user provided one — `vitruvio new` does not set it.
|
||||
- If a mobile form was created, add `"mobile": "processes/<key>/<key>-mobile.xml"` inside `"forms"`.
|
||||
- Add `schedules` only if the process runs on a timer (cron/simple interval).
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- Files created: `<key>.bpmn`, `<key>-desktop.xml` (and `<key>-mobile.xml` if applicable).
|
||||
- Registered in `vitruvio.json` with key `<key>`.
|
||||
- The process identity is the `<bpmn2:process id>` — it must match the key.
|
||||
- Each `activiti:formKey` in the BPMN must have a matching `<form formKey="...">` in **every**
|
||||
form file (desktop and mobile).
|
||||
- Submitted field `id="X"` in `formKey="formAbertura"` becomes process variable `formAbertura_X`
|
||||
(desktop auto-injects these; mobile must declare/fetch them — see vitruvio-criar-form-mobile).
|
||||
- For complex flows, recommend editing the BPMN in bpmn.io or Camunda Modeler before deploying.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Create Vitruvio Query
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating a new named SQL query inside a Vitruvio repository. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect query details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing from their original request):
|
||||
|
||||
- **Key** — kebab-case unique identifier. Used to reference this query in reports, DBTable components, and scripts.
|
||||
- **Name** — human-readable label shown in the Vitruvio UI.
|
||||
- **SQL** — the query itself, or enough context to write it.
|
||||
- **Connection** — datasource name (default: `vitruvio_producao`). Ask only if the user mentions a specific datasource.
|
||||
|
||||
## Step 3 — Create the file
|
||||
|
||||
Target path: `queries/<key>.sql`
|
||||
|
||||
Rules:
|
||||
- One `SELECT` per file. No multiple statements, no DDL, no INSERT/UPDATE/DELETE.
|
||||
- Named bind parameters use `:paramName` syntax — never concatenate user input into SQL.
|
||||
- Write ANSI SQL where possible. If DB-specific syntax is unavoidable, note it in a comment.
|
||||
- Keep Oracle and PostgreSQL compatibility in mind — avoid syntax that only works in one.
|
||||
|
||||
```sql
|
||||
SELECT col1,
|
||||
col2
|
||||
FROM my_table
|
||||
WHERE active = 1
|
||||
AND id = :id
|
||||
ORDER BY col1
|
||||
```
|
||||
|
||||
## Step 4 — Register in vitruvio.json
|
||||
|
||||
Read `vitruvio.json`, find or create the `"queries"` array, and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"source": "queries/<key>.sql",
|
||||
"connection": "<connection>"
|
||||
}
|
||||
```
|
||||
|
||||
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created: `queries/<key>.sql`
|
||||
- Registered in `vitruvio.json` with key `<key>`
|
||||
- How it can be used: as a datasource in a report, in a DBTable component, or loaded in a script via `db.executeNamedQuery('<key>', params)`
|
||||
@@ -0,0 +1,118 @@
|
||||
# Create Vitruvio Report
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are registering a new report inside a Vitruvio repository. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Identify the report type
|
||||
|
||||
Ask the user which type applies (if not already clear from the request):
|
||||
|
||||
| Type | When to use |
|
||||
|------|-------------|
|
||||
| **MODELO_ESTATICO** | A Jasper report whose layout is fully designed in a `.jrxml` file (Jaspersoft Studio). The dev provides or will provide the `.jrxml`. |
|
||||
| **DINAMICO_QUERY_SQL** | A Vitruvio-managed report where data comes from a named query and columns/layout are configured in `vitruvio.json`. The `.jrxml` is still needed but is simpler — Vitruvio drives the structure. |
|
||||
|
||||
## Step 3 — Collect details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing):
|
||||
|
||||
**Both types:**
|
||||
- **Key** — kebab-case or snake_case unique identifier.
|
||||
- **Name** — human-readable label shown in the UI.
|
||||
- **Category** — display category (e.g. `"Compras"`, `"Auditoria/Gondola"`).
|
||||
- **Owner** — Vitruvio username of the responsible person.
|
||||
- **Orientation** — `RETRATO` (portrait) or `PAISAGEM` (landscape). Default: `RETRATO`.
|
||||
- **Allowed groups / users** — who can access this report (can be empty arrays).
|
||||
- **Has parameters?** — does the user fill in parameters before running it? If yes, a params form is needed.
|
||||
|
||||
**DINAMICO_QUERY_SQL only:**
|
||||
- **Query key** — the named query that feeds the report (must be registered in `vitruvio.json`).
|
||||
- **Columns** — list of columns: name (DB column), label, alignment (`LEFT`/`CENTER`/`RIGHT`), width (px), aggregation (`null`, `SUM`, `COUNT`, etc.).
|
||||
|
||||
## Step 4 — Create the files
|
||||
|
||||
### MODELO_ESTATICO
|
||||
|
||||
Files live flat in `reports/`:
|
||||
- `reports/<key>.jrxml` — **do not generate this file**; tell the user to place the Jaspersoft-designed template here. Must target **JasperReports 6.21.2** — do not save with a newer version.
|
||||
- `reports/<key>-params.xml` — only if the report has parameters (follows the same Vaadin XML form schema as panels).
|
||||
|
||||
### DINAMICO_QUERY_SQL
|
||||
|
||||
Files live in a subdirectory:
|
||||
- `reports/<key>/template.jrxml` — **do not generate this file**; tell the user to place the template here.
|
||||
- `reports/<key>/params.xml` — only if the report has parameters.
|
||||
|
||||
## Step 5 — Register in vitruvio.json
|
||||
|
||||
Read `vitruvio.json`, find or create the `"reports"` array, and add the entry.
|
||||
|
||||
### MODELO_ESTATICO entry
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"type": "MODELO_ESTATICO",
|
||||
"category": "<category>",
|
||||
"owner": "<owner>",
|
||||
"template": "reports/<key>.jrxml",
|
||||
"parameterForm": "reports/<key>-params.xml",
|
||||
"orientation": "RETRATO",
|
||||
"allowedGroups": [],
|
||||
"allowedUsers": [],
|
||||
"columns": [],
|
||||
"schedules": []
|
||||
}
|
||||
```
|
||||
|
||||
Omit `"parameterForm"` if no params form.
|
||||
|
||||
### DINAMICO_QUERY_SQL entry
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"type": "DINAMICO_QUERY_SQL",
|
||||
"category": "<category>",
|
||||
"owner": "<owner>",
|
||||
"template": "reports/<key>/template.jrxml",
|
||||
"parameterForm": "reports/<key>/params.xml",
|
||||
"query": "<query-key>",
|
||||
"orientation": "RETRATO",
|
||||
"allowedGroups": [],
|
||||
"allowedUsers": [],
|
||||
"columns": [
|
||||
{
|
||||
"name": "COLUMN_NAME",
|
||||
"label": "Column Label",
|
||||
"align": "LEFT",
|
||||
"width": 100,
|
||||
"aggregation": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Omit `"parameterForm"` if no params form.
|
||||
|
||||
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
|
||||
|
||||
## Step 6 — Report
|
||||
|
||||
Tell the user:
|
||||
- Entry registered in `vitruvio.json` with key `<key>` and type `<type>`
|
||||
- For MODELO_ESTATICO: remind them to place the `.jrxml` at `reports/<key>.jrxml`, designed in Jaspersoft Studio 6.21.2
|
||||
- For DINAMICO_QUERY_SQL: remind them to place the template at `reports/<key>/template.jrxml`
|
||||
- If a params form is needed: what file to create and that it follows the same Vaadin XML schema as panels
|
||||
@@ -0,0 +1,97 @@
|
||||
# Create Vitruvio Script
|
||||
|
||||
> All messages shown to the user must be written in Portuguese.
|
||||
|
||||
You are creating a new script inside a Vitruvio repository. Follow these steps in order.
|
||||
|
||||
## Step 1 — Confirm you are inside a Vitruvio repo
|
||||
|
||||
```bash
|
||||
ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO"
|
||||
```
|
||||
|
||||
If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo.
|
||||
|
||||
## Step 2 — Collect script details
|
||||
|
||||
Ask the user (in a single message, only ask what is missing from their original request):
|
||||
|
||||
- **Key (sigla)** — unique identifier used in `libService.loadScript('key')`. Snake_case. Must be unique across all scripts in the repo. If the user already provided a name, suggest a key derived from it.
|
||||
- **Name** — human-readable label shown in the Vitruvio UI.
|
||||
- **Pattern** — which of the two patterns applies:
|
||||
- **Library** — reusable module, loaded by other scripts/endpoints/panels via `libService.loadScript`. Wrap in `({...})`.
|
||||
- **Process/task script** — runs directly from a process task or scheduler. Top-level execution, no export.
|
||||
- **Description** — one sentence about what this script does (optional, but ask if not provided).
|
||||
- **Domain** — `USUARIO` (user-level, default) or `SISTEMA` (system-level).
|
||||
|
||||
## Step 3 — Create the file
|
||||
|
||||
Target path: `scripts/<key>.js`
|
||||
|
||||
### Library template
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Nome: <name>
|
||||
* Sigla: <key>
|
||||
* Descrição: <description>
|
||||
*/
|
||||
({
|
||||
// example function — replace with actual implementation
|
||||
run: function(params) {
|
||||
var db = libService.loadScript('db');
|
||||
var banco = new db(db.VITRUVIO_DATASOURCE);
|
||||
|
||||
// implementation here
|
||||
|
||||
return {};
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Process / task script template
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Nome: <name>
|
||||
* Sigla: <key>
|
||||
* Descrição: <description>
|
||||
*/
|
||||
(function(execution) {
|
||||
var db = libService.loadScript('db');
|
||||
var banco = new db(db.VITRUVIO_DATASOURCE);
|
||||
|
||||
// implementation here
|
||||
})(execution)
|
||||
```
|
||||
|
||||
Rules (Rhino ES5 — no exceptions):
|
||||
- No `let`, `const`, arrow functions, template literals, destructuring, spread, `class`, or `import/export`
|
||||
- Use `var` everywhere
|
||||
- No `require`, `process`, `window`, or Node/browser globals
|
||||
- String concatenation with `+`, not template literals
|
||||
- `JSON.parse` / `JSON.stringify` for serialization
|
||||
|
||||
## Step 4 — Register in vitruvio.json
|
||||
|
||||
Read `vitruvio.json`, find or create the `"scripts"` array, and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "<key>",
|
||||
"name": "<name>",
|
||||
"description": "<description>",
|
||||
"language": "javascript",
|
||||
"domain": "<USUARIO|SISTEMA>",
|
||||
"source": "scripts/<key>.js"
|
||||
}
|
||||
```
|
||||
|
||||
Preserve the existing file structure and all other entries. Write the updated `vitruvio.json` back.
|
||||
|
||||
## Step 5 — Report
|
||||
|
||||
Tell the user:
|
||||
- File created: `scripts/<key>.js`
|
||||
- Registered in `vitruvio.json` with key `<key>`
|
||||
- How to load it from another script or endpoint: `var lib = libService.loadScript('<key>');`
|
||||
Reference in New Issue
Block a user