Buscar K
Aparência
Aparência
Um writeback é um Cadastro: uma tabela cujas linhas as pessoas digitam num formulário da tela, sem Dataflow. O arquivo fica em writebacks/<slug>--<id>.yaml e guarda só o esquema (campos, tipos, seções, layout); as linhas nunca entram no YAML. O que é um Cadastro e quando usar está em Cadastros.
lumo new writeback --name "Metas por Filial"
lumo new writeback --name "Metas por Filial" --from-file metas.xlsx # cria e já carrega a planilhaDepois de editar, lumo push writeback:<id> e, uma vez, lumo writeback publish writeback:<id> --desk <dw-desk>.
fileName^[a-z0-9-]+--\d+\.yaml$headerheader.idheader.kindwriteback (fixo)header.lumov2 (fixo)header.tenantIdheader.appIdheader.dashboardIdbodybody.namebody.alias^[A-Z][A-Z0-9_]{0,44}$lumo push rejects the whole request with a conflict (WB_ALIAS_READONLY) if this value differs from the server's stored alias; revert it to the value from the last lumo pull/fetch and push again. To rename the table shown to users, edit name instead - alias is the internal identifier, not the label.body.deskIdbody._statedraft, published, inconsistentbody.sectionsbody.gridgrid block, which controls that one column's visibility/order/width).body.grid.frozen_countbody.hooksbody.hooks.on_saverecord.ATUALIZADO_EM = new Date().toISOString();). null/omitted = no hook. This is arbitrary logic, not a cosmetic setting - changing it changes what gets persisted on every future save of this Cadastro.body.unique_constraintsbody.fieldslumo push requires --rename/--allow-drop to acknowledge that explicitly.body.fields[].alias^[A-Z][A-Z0-9_]{0,44}$H_ prefix is reserved for system columns and WB_ID is the reserved row-id column - both are rejected here. Renaming an existing field's alias is a destructive drop+add unless declared with lumo push --rename OLD=NEW; the schema cannot see that it is a rename versus a genuinely new field, only the server's diff against the stored definition can.body.fields[].labelbody.fields[].typenumber, date, string, external_key, image, picklistgranularity. external_key additionally requires ref (a lookup into another table, rendered as a search/select in the form). picklist additionally requires a non-empty options list (rendered as a fixed dropdown). Changing type after the field has data can be destructive depending on whether the underlying physical data type changes (e.g. string→number) - the server classifies this at push time; the schema only enforces which companion keys each type requires.body.fields[].maskbody.fields[].optionstype: picklisttype: picklist - the fixed list of selectable values. Ignored for every other type; the server does not clear it if you leave it on a field you retype away from picklist, so remove it yourself when changing type to avoid a stale list.body.fields[].options[].valueoptions - the schema cannot check that, the server does.body.fields[].options[].colorbody.fields[].tooltipbody.fields[].requiredbody.fields[].defaultbody.fields[].validationif (value < 0) return "Meta não pode ser negativa";). null/omitted = no extra validation beyond the type/required checks.body.fields[].granularitytype: date · valores: year, month, date, datetimetype: date - the precision the form captures and the column stores (year picker vs. full date vs. date+time, etc.). Ignored and meaningless for every other type.body.fields[].reftype: external_keytype: external_key - points this field at another table's key column, rendered as a lookup/search picker in the form. Ignored for every other type.body.fields[].ref.tabletableId, not by this string - if they disagree, tableId wins.body.fields[].ref.tableIdbody.fields[].ref.keybody.fields[].ref.key_typeString, Number, Datekey in the referenced table - determines the stored data type of this field itself. Defaults to "String" when omitted; set it explicitly whenever the referenced key is Number or Date, since a wrong value here mismatches the physical column type.body.fields[].ref.label_exprbody.fields[].sectionsections list, though the server does not enforce that match. null/omitted = ungrouped.body.fields[].gridgrid block, which only controls frozen columns).body.fields[].grid.visiblebody.fields[].grid.ordergrid.order. Defaults to 0 when omitted.body.fields[].grid.widthbody.fields[].grid.formatbody.fields[].grid.alignleft, right, centerbody.expressionsfields, and are not part of the data-entry form; they only affect how this Cadastro is consumed by dashboards/apps. column_name must not collide with WB_ID, with any fields[].alias, or with another expression's column_name - the schema can only reject the literal value WB_ID; the cross-checks against fields[] and against sibling expressions are server-side, at push time.body.expressions[].column_name^[A-Z][A-Z0-9_]{0,44}$fields[].alias or with another expression's column_name - the schema cannot check either, the server does, at push time.body.expressions[].labelbody.expressions[].data_typeNumber, Date, Stringexpression actually produces - the server does not infer or coerce this from the SQL.body.expressions[].expressionbody.expressions[].maskbody.expressions[].behaviordefault, yearmonth, year, agp, avg, datetimebody.metadata_bibody.metadata_bi.enabledbody.metadata_bi.hidden_columnsenabled is true - an individual opt-out, e.g. hide H_WB_EDITOR while still exposing H_WB_CREATED_AT. Has no effect while enabled is false.# writebacks/metas-por-filial--412.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/writeback.schema.json
id: 412
kind: writeback
lumo: v2
tenantId: 1200
---
name: Metas por Filial
alias: METAS_FILIAL_412 # nome físico, gerado pelo servidor; não mude
sections:
- Identificação
- Meta
fields:
- alias: FILIAL
label: Filial
type: external_key # valores vêm de uma tabela do HorusDW
required: true
section: Identificação
ref:
table: dim_filiais
tableId: 6105 # o servidor liga por este id
key: ID_FILIAL
key_type: Number
label_expr: NOME_FILIAL # coluna mostrada no seletor
- alias: PERIODO
label: Período
type: date
granularity: month # obrigatório em type: date
required: true
section: Identificação
- alias: META_VALOR
label: Meta (R$)
type: number
mask: MOEDA
required: true
section: Meta
- alias: STATUS
label: Status
type: picklist # vocabulário fechado, escrito aqui
required: true
section: Meta
options:
- value: Pendente
- value: Aprovado
unique_constraints:
- [FILIAL, PERIODO] # uma meta por filial e mêstype | Para quê | Exige |
|---|---|---|
string | Texto livre | |
number | Número; a máscara vai em mask | |
date | Data | granularity: year, month, date ou datetime |
external_key | Escolher uma linha de outra tabela do HorusDW (uma filial, um cliente) | ref com tableId, key e label_expr |
picklist | Escolher num vocabulário fechado definido no próprio YAML (um status) | options com pelo menos um item |
image | Enviar uma imagem |
fields[] é o esquema. Acrescentar um item e dar push cria a coluna física; tirar um item apaga a coluna e os dados dela. Por isso o push recusa quando um alias some de fields[] e pede que você diga o que foi: --rename ANTIGO=NOVO preserva os dados, --allow-drop ANTIGO apaga. Veja Drift e conflitos.alias do topo do corpo é o nome físico da tabela, que outros Cadastros referenciam pelo texto. O push que o muda é recusado sempre. Para mudar o nome que as pessoas veem, edite name.type de um campo que já tem dados pode perder os dados, conforme o tipo físico mude ou não. Confira com lumo writeback preview depois.key_type nem key_columns: a identidade da linha é a coluna reservada WB_ID, e nenhum item de expressions pode se chamar assim.push. Publicar de novo é recusado pelo servidor; não existe clone de Cadastro.--desk do publish é de Mesa de Dados (--desk 7 ou --desk dw-desk:7). Uma referência bi-desk:<id> é recusada antes de chegar ao servidor. Como os ids de Mesa de Dados e de Aplicação se repetem, confira o número com lumo list dw-desk.lumo writeback import, imports, rollback-import e export servem para montar o projeto (carregar a planilha que o cliente mandou, desfazer uma carga errada), não para a digitação do dia a dia. Não rode import com a mesma planilha que já foi no --from-file: as linhas entram duas vezes.