@m3r/sdk 0.5.0
ctx.ext
Tabela, tela, menu, relatório e records da extensão. JSONB ou tabela física FAX####. O ERP pinta.
ctx.ext não mistura com ctx.m3r.sales. Você declara schema e o host /apps/extensions/{code} renderiza DataTable, Modal e PageHeader.
O index.ts não declara schema. Ele chama registerVendas(ctx). Cada ensure / define vive na pasta do domínio. Árvore e ordem: Padrões.
m3r.json precisa listar os mesmos tables[], screens[], nav[], reports[] de todos os arquivos em src/**. Validate varre o fonte, não só o index.
Limites (0.5.0)
| Recurso | Teto | Se estourar |
|---|---|---|
| Tabelas / extensão / tenant | 20 | SdkValidationError |
| Colunas / tabela | 30 | idem |
| Bytes / linha | 64 KB | API recusa |
| Itens de menu | 20 | SdkValidationError |
| Relatórios | 10 | idem |
tables.ensure
| Campo | Tipo | Regra |
|---|---|---|
name | string | Slug [a-z][a-z0-9_]{1,47}. Id lógico. Sem SQL identifier do Core. |
label | string | Título. |
columns[].name | string | Mesmo slug. Reservados abaixo. |
columns[].type | SdkColumnType | string, text, number, boolean, date, enum. |
columns[].options | string[] | Obrigatório se enum. |
columns[].required | boolean | Default false. |
uniqueKeys | string[][] | Opcional. Physical = UNIQUE no Postgres (WHERE delete_date IS NULL). Document = o Core compara as linhas JSONB vivas. |
storage | SdkTableStorage | Document (padrão, JSONB em FADEV07) ou Physical (tabela FAX#### criada pelo Core). |
physicalName | FAX0001–FAX9999 | Opcional. Se omitir, sequence fadev_physical_table_seq — pula código já ocupado — e devolve no retorno. Não muda depois. |
O name é o id lógico. O nome físico não é um identificador SQL seu: ou o Core atribui FAX0001…FAX9999, ou você pede um physicalName livre nesse formato. Depois do primeiro ensure, não muda. Toda tabela física nasce com id, company_id, extension_id, created_by, created_at, updated_at, delete_date. Esses nomes são reservados — não declare.
Evolução: só adicionar coluna. Sem drop, sem rename, sem mudar tipo, sem trocar storage. Sem FK para tabela do Core (FAT003, financeiro, …). Isolamento do dado: companyId + extensionId. FADEV02 (development / staging / production) não cria banco separado.
Não pode: json, uuid, FK para FAT003, SQL, escolher o código FA*.
screens.define
| Campo | Tipo | Regra |
|---|---|---|
code | string | Slug [a-z][a-z0-9_.-]{1,62}. Vira URL /apps/extensions/{code}. |
kind | SdkScreenKind | Page ou Modal. |
table | string | Já passou por ensure. |
list.columns | string[] | Colunas da DataTable. |
form.fields | string ou objeto | bind só SdkFormBind. |
actions | { id, label, opens? }[] | Opcional. |
SdkFormBind: document.id, document.code, document.status, document.observation, document.customerName, document.customerId, document.deliveryDate, document.orderDocType.
Qualquer outro path: assertFormBind → SdkValidationError.
Depois do mount: IP.EXT.SCREEN.ON_LOAD. Dá para ui.setField / ui.disableField / ui.hideField. Não nega o load.
actions[].opens aceita código de tela ou prefixo explícito: openScreen:, openModal:, openReport:. Sem prefixo, o host trata como tela. Relatório vai para /apps/extensions/reports/{code}. Se a origem for venda, a URL da tela leva documentId / documentCode e o ON_LOAD recebe o documento.
records
CRUD na sua tabela. Isolado por empresa + extensão.
| Método | Entrada |
|---|---|
find(table, { limit?, cursor? }) | página das linhas vivas |
get(table, id) | uma linha (id, tableName, data, createdBy, createdAt, updatedAt) |
create(table, data) | objeto das colunas; recusa se uniqueKeys colidir |
update(table, id, data) | patch do data; mesma checagem de unique |
remove(table, id) | soft delete — delete_date preenchido. A linha some do find e da tela. Unique volta a aceitar o mesmo valor. |
O CRUD da tela no ERP usa as mesmas regras. Dá para gravar no AFTER_SAVE ou num ctx.events.on — a sample 0.6.1 faz isso na auditoria. Não leia pedido de venda por aqui — use ctx.m3r.
Extensão desativada no tenant recusa tela, relatório e records.
nav.define
Sem URL livre. Alvo = tela ou relatório seu.
| Campo | Tipo | Regra |
|---|---|---|
id / label | string | Em nav[]. Mesmo slug de tela ([a-z][a-z0-9_.-]{1,62}). |
icon | string? | MDI (mdi:…). |
target.type | SdkNavTarget | Screen ou Report. |
target.code | string | Código já definido. |
placement.parent | SdkMenuParent | Lista fechada — ver Padrões. |
placement.section | SdkNavSection | Só Extensions. Ou parent, nunca os dois. |
parent: "Vendas" (errado) não compile. É SdkMenuParent.Vendas → "vendas".
reports.define
Fonte: só tabela da extensão. PDF = gerador tabular do Core (farmus-reports). Sem Python na extensão.
| Campo | Tipo | Regra |
|---|---|---|
code / title | string | Host /apps/extensions/reports/{code}. |
table | string | Sua tabela. |
columns | { key, label }[] | key = coluna. |
filters | string ou { type, column?, label? } | DateRange (coluna date ou createdAt), Enum, Text. |
| export | — | PDF e Excel do mesmo recorte. Sem script Python. |
Pode / não pode
Pode
- Persistência isolada para dado da extensão.
- Menu no grupo Vendas e no grupo Extensões (dois
define). - PDF e Excel do que está na tabela, com filtro na coluna declarada.
Não pode
- SQL em
FAT003/ financeiro. - React, iframe, coluna
json. - Relatório com query no Core.
placement.parent: "foo".
