@oondemand/create-central-oon 0.3.45 → 0.3.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,256 @@
1
+ # Manifesto backend de processos
2
+
3
+ O arquivo opcional `backend/central.process.json` declara capacidades de processo que pertencem ao OonCore e não à implementação local de uma Central.
4
+
5
+ Ele complementa:
6
+
7
+ - `central.app.json`: identidade, módulos e capabilities da aplicação;
8
+ - `backend/central.domain.json`: models, campos, fórmulas do próprio registro e validações locais;
9
+ - `frontend/central.ui.json`: projeção visual, coleções, esteiras e ações exibidas.
10
+
11
+ O manifesto de processos é carregado **depois** do domínio. Por isso, toda model e todo campo citados precisam existir em `central.domain.json`.
12
+
13
+ ## Contrato mínimo
14
+
15
+ ```json
16
+ {
17
+ "schemaVersion": 1,
18
+ "models": {
19
+ "Pagamento": {
20
+ "workflow": {},
21
+ "bindings": [],
22
+ "deleteProtection": [],
23
+ "atomicInvariants": []
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ O runtime não executa JavaScript, `eval`, nomes de funções ou módulos informados no JSON. Expressões usam a mesma AST fechada das regras de domínio.
30
+
31
+ ## Workflow e transições
32
+
33
+ ```json
34
+ {
35
+ "workflow": {
36
+ "stageField": "etapa",
37
+ "initialStages": ["Solicitado"],
38
+ "defaultStage": "Solicitado",
39
+ "transitions": [
40
+ { "from": "Solicitado", "to": "Aprovado" },
41
+ {
42
+ "from": "Aprovado",
43
+ "to": "Aguardando NF",
44
+ "when": {
45
+ "op": "eq",
46
+ "args": [
47
+ { "field": "aprovadoFinanceiro" },
48
+ { "value": true }
49
+ ]
50
+ }
51
+ }
52
+ ],
53
+ "lockedFieldsByStage": {
54
+ "Enviado para Omie": ["valor", "projetoId", "projetoItemId"],
55
+ "Pagamento Ok": ["valor", "projetoId", "projetoItemId"]
56
+ },
57
+ "lockedMessage": "Os dados de negócio ficam bloqueados nesta etapa.",
58
+ "onEnter": [
59
+ {
60
+ "stage": "Enviado para Omie",
61
+ "set": {
62
+ "statusTrabalho": "Trabalhando",
63
+ "omieStatusIntegracao": "Pendente"
64
+ }
65
+ }
66
+ ],
67
+ "automaticTransitions": [
68
+ {
69
+ "when": {
70
+ "op": "eq",
71
+ "args": [
72
+ { "field": "omieLiquidado" },
73
+ { "value": true }
74
+ ]
75
+ },
76
+ "to": "Pagamento Ok",
77
+ "set": { "statusTrabalho": "Trabalhando" }
78
+ }
79
+ ]
80
+ }
81
+ }
82
+ ```
83
+
84
+ Regras importantes:
85
+
86
+ - o backend compara o valor anterior e o novo; enviar novamente a mesma etapa não cria uma transição;
87
+ - uma mudança manual precisa existir em `transitions` e satisfazer `when`;
88
+ - `lockedFieldsByStage` é aplicado sobre a etapa anterior e impede alterações de negócio mesmo por `PUT` ou `PATCH` diretos;
89
+ - `onEnter` produz alterações confiáveis do Core;
90
+ - `automaticTransitions` é executado pelo servidor depois dos bindings e não depende do frontend.
91
+
92
+ A UI pode continuar declarando botões `transition` e `setField`. Ela é uma projeção; a autoridade permanece no backend.
93
+
94
+ ## Bindings cross-model
95
+
96
+ Bindings preenchem campos derivados a partir de outra model ou de registros relacionados. Todos são resolvidos em lote.
97
+
98
+ ### Lookup
99
+
100
+ ```json
101
+ {
102
+ "field": "percentualFeeAplicado",
103
+ "kind": "lookup",
104
+ "sourceModel": "Projeto",
105
+ "localField": "projetoId",
106
+ "sourceField": "percentualFee",
107
+ "watchFields": ["percentualFee"],
108
+ "recalculate": "async",
109
+ "default": 0
110
+ }
111
+ ```
112
+
113
+ Quando `Projeto.percentualFee` muda, o Core encontra os itens dependentes e agenda um recálculo assíncrono. O recálculo usa uma leitura por binding e um `bulkWrite`, em vez de executar `save()` item a item na requisição do usuário.
114
+
115
+ ### Agregação de relacionamento
116
+
117
+ ```json
118
+ {
119
+ "field": "pagamentoTotalPlanejado",
120
+ "kind": "aggregate",
121
+ "sourceModel": "Pagamento",
122
+ "foreignField": "projetoItemId",
123
+ "operator": "sum",
124
+ "sourceField": "valor",
125
+ "match": {
126
+ "canceladoNaCentral": { "neq": true }
127
+ },
128
+ "default": 0
129
+ }
130
+ ```
131
+
132
+ Operadores disponíveis:
133
+
134
+ - `sum`;
135
+ - `count`;
136
+ - `min`;
137
+ - `max`.
138
+
139
+ Filtros aceitam igualdade direta ou `{ "eq": ... }`, `{ "neq": ... }`, `{ "in": [...] }` e `{ "nin": [...] }`.
140
+
141
+ ### Expressão derivada
142
+
143
+ ```json
144
+ {
145
+ "field": "pagamentoValorPendente",
146
+ "kind": "expression",
147
+ "precision": 2,
148
+ "expression": {
149
+ "op": "max",
150
+ "args": [
151
+ { "value": 0 },
152
+ {
153
+ "op": "subtract",
154
+ "args": [
155
+ { "field": "contratacaoTotal" },
156
+ { "field": "pagamentoTotalPago" }
157
+ ]
158
+ }
159
+ ]
160
+ }
161
+ }
162
+ ```
163
+
164
+ Bindings são avaliados na ordem declarada. Assim, uma expressão pode consumir lookups e agregações anteriores.
165
+
166
+ Além dos operadores numéricos e lógicos do domínio, processos podem usar:
167
+
168
+ - `if`: condição, valor verdadeiro e valor falso;
169
+ - `concat`: concatenação segura de valores;
170
+ - `formatCurrency`: valor, moeda opcional e locale opcional.
171
+
172
+ ## Recálculo imediato e assíncrono
173
+
174
+ `recalculate` controla a reação quando a model de origem muda:
175
+
176
+ - `immediate` (padrão): dependentes são atualizados no mesmo ciclo da mutação;
177
+ - `async`: a resposta não percorre todos os dependentes; o Core coloca o recálculo na fila interna e usa operações em lote.
178
+
179
+ Use `async` para alterações de um pai com muitos filhos, como a mudança de percentuais de um Projeto. Use `immediate` quando o registro pai precisa refletir a alteração antes da resposta, como o resumo de pagamentos de um item.
180
+
181
+ A API `drainProcessJobs()` existe para testes e homologações determinísticas.
182
+
183
+ ## Proteção declarativa de exclusão
184
+
185
+ ```json
186
+ {
187
+ "deleteProtection": [
188
+ {
189
+ "sourceModel": "Pagamento",
190
+ "foreignField": "projetoItemId",
191
+ "message": "Não é possível excluir o item porque existem pagamentos vinculados."
192
+ }
193
+ ]
194
+ }
195
+ ```
196
+
197
+ A verificação ocorre no CRUD oficial antes de `findByIdAndDelete`. Não é necessário sobrescrever métodos do Mongoose.
198
+
199
+ ## Invariável financeira atômica
200
+
201
+ ```json
202
+ {
203
+ "atomicInvariants": [
204
+ {
205
+ "name": "pagamentos-limitados-ao-contratado",
206
+ "kind": "relatedSumLteParentField",
207
+ "parentModel": "ProjetoItem",
208
+ "parentLocalField": "projetoItemId",
209
+ "sourceField": "valor",
210
+ "parentField": "contratacaoTotal",
211
+ "match": {
212
+ "canceladoNaCentral": { "neq": true }
213
+ },
214
+ "tolerance": 0.01,
215
+ "code": "PAGAMENTO_ACIMA_CONTRATADO",
216
+ "message": "A soma {total} não pode ultrapassar o valor contratado {limit}."
217
+ }
218
+ ]
219
+ }
220
+ ```
221
+
222
+ O Core executa a mutação em transação MongoDB e incrementa uma versão interna no registro pai antes de calcular a soma. Duas inclusões simultâneas disputam a mesma escrita do pai:
223
+
224
+ 1. uma transação conclui;
225
+ 2. a outra recebe conflito transitório;
226
+ 3. o Core repete a transação;
227
+ 4. a soma é refeita já considerando a primeira inclusão;
228
+ 5. a segunda inclusão é aceita ou rejeitada pela invariável.
229
+
230
+ Uma simples validação `find + sum + save`, fora de transação, **não** oferece essa garantia.
231
+
232
+ ## Alterações reais
233
+
234
+ O contexto enviado a `defineValidation` agora inclui `changedFields`. A lista contém somente campos cujo valor final difere do registro anterior, incluindo valores derivados pelo Core. Isso evita efeitos colaterais acionados por round-trips de campos sem alteração real.
235
+
236
+ ## Fronteira recomendada
237
+
238
+ Pertence ao Core/process manifest:
239
+
240
+ - transições e bloqueios de etapa;
241
+ - status operacionais recorrentes;
242
+ - dependências e recálculos cross-model;
243
+ - agregações relacionadas;
244
+ - proteção de exclusão;
245
+ - invariáveis concorrentes;
246
+ - processamento em lote/assíncrono.
247
+
248
+ Permanece na Central:
249
+
250
+ - fórmula comercial específica;
251
+ - tipos de responsáveis permitidos pelo negócio;
252
+ - regras fiscais específicas;
253
+ - integrações e mapeamentos particulares;
254
+ - mensagens e condições próprias do processo.
255
+
256
+ A Central declara essas particularidades usando o contrato; não reimplementa o mecanismo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oondemand/create-central-oon",
3
- "version": "0.3.45",
3
+ "version": "0.3.47",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/oondemand/oon-platform.git",
@@ -12,6 +12,6 @@
12
12
  "deploy": "oonCore-back deploy"
13
13
  },
14
14
  "dependencies": {
15
- "@oondemand/oon-core-back": "^0.3.45"
15
+ "@oondemand/oon-core-back": "^0.3.47"
16
16
  }
17
17
  }
@@ -10,7 +10,7 @@
10
10
  "sync:metadata": "oonCore-front sync:metadata"
11
11
  },
12
12
  "dependencies": {
13
- "@oondemand/oon-core-front": "^0.3.45",
13
+ "@oondemand/oon-core-front": "^0.3.47",
14
14
  "@chakra-ui/react": "^3.13.0",
15
15
  "@emotion/react": "^11.14.0",
16
16
  "@tanstack/react-query": "^5.65.0",