@pacp/spec 3.3.0 → 3.4.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/schema.ts","../src/validate.ts"],"sourcesContent":["export { schema, profiles, profileIds } from \"./schema.js\";\r\nexport { validate } from \"./validate.js\";\r\nexport type {\r\n ScalarValue,\r\n ImageType,\r\n Image,\r\n Measure,\r\n PhysicalDimensions,\r\n AttributeRef,\r\n AttributeValue,\r\n Option,\r\n LotPolicy,\r\n SalesUnit,\r\n Product,\r\n Predicate,\r\n Condition,\r\n Component,\r\n RuleOperation,\r\n Rule,\r\n Ruleset,\r\n LookupAxis,\r\n TableRow,\r\n Table,\r\n Dependency,\r\n Constraint,\r\n PriceList,\r\n Catalog,\r\n ProductRef,\r\n Context,\r\n Pricing,\r\n CatalogDocument,\r\n ProductDocument,\r\n PacpDocument,\r\n ProfileId,\r\n ValidationIssue,\r\n ValidationResult,\r\n} from \"./types.js\";\r\n","import { readFileSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\n\r\nfunction loadJson(relativePath: string): Record<string, unknown> {\r\n const fullPath = join(__dirname, relativePath);\r\n return JSON.parse(readFileSync(fullPath, \"utf8\"));\r\n}\r\n\r\nexport const schema = loadJson(\"./pacp.schema.json\") as Record<string, unknown>;\r\n\r\nexport const profiles = {\r\n moveis: loadJson(\"./profiles/moveis.schema.json\") as Record<string, unknown>,\r\n iluminacao: loadJson(\"./profiles/iluminacao.schema.json\") as Record<string, unknown>,\r\n \"pisos-revestimentos\": loadJson(\"./profiles/pisos-revestimentos.schema.json\") as Record<string, unknown>,\r\n \"fiscal-br\": loadJson(\"./profiles/fiscal-br.schema.json\") as Record<string, unknown>,\r\n} as const;\r\n\r\nexport type ProfileId = keyof typeof profiles;\r\n\r\nexport const profileIds = Object.keys(profiles) as ProfileId[];\r\n","import type { ValidationResult, ValidationIssue, PacpDocument } from \"./types.js\";\r\nimport { schema, profiles, type ProfileId } from \"./schema.js\";\r\n\r\nexport function validate(document: unknown): ValidationResult {\r\n let Ajv2020: typeof import(\"ajv/dist/2020\").default;\r\n let addFormats: typeof import(\"ajv-formats\").default;\r\n\r\n try {\r\n Ajv2020 = require(\"ajv/dist/2020\") as typeof import(\"ajv/dist/2020\").default;\r\n addFormats = require(\"ajv-formats\") as typeof import(\"ajv-formats\").default;\r\n } catch {\r\n throw new Error(\r\n 'Para usar validate(), instale ajv e ajv-formats: npm install ajv ajv-formats'\r\n );\r\n }\r\n\r\n const ajv = new Ajv2020({ allErrors: true, strict: false });\r\n addFormats(ajv);\r\n\r\n const issues: ValidationIssue[] = [];\r\n\r\n const validateSchema = ajv.compile(schema);\r\n const valid = validateSchema(document);\r\n\r\n if (!valid && validateSchema.errors) {\r\n for (const error of validateSchema.errors) {\r\n issues.push({\r\n code: \"SCHEMA\",\r\n path: error.instancePath || \"/\",\r\n message: error.message ?? \"Erro de validacao de schema\",\r\n });\r\n }\r\n }\r\n\r\n if (document && typeof document === \"object\" && !Array.isArray(document)) {\r\n const doc = document as Record<string, unknown>;\r\n const declaredProfiles = Array.isArray(doc.profiles) ? doc.profiles : [];\r\n\r\n for (const profileId of declaredProfiles) {\r\n if (typeof profileId !== \"string\") continue;\r\n\r\n const profileSchema = profiles[profileId as ProfileId];\r\n if (!profileSchema) {\r\n issues.push({\r\n code: \"UNKNOWN_PROFILE\",\r\n path: \"/profiles\",\r\n message: `Profile \"${profileId}\" nao e um profile oficial PACP`,\r\n });\r\n continue;\r\n }\r\n\r\n const products = extractProducts(doc);\r\n const validateProfile = ajv.compile(profileSchema);\r\n\r\n for (let i = 0; i < products.length; i++) {\r\n const xFields = extractXFields(products[i]);\r\n if (Object.keys(xFields).length === 0) continue;\r\n\r\n const profileValid = validateProfile(xFields);\r\n if (!profileValid && validateProfile.errors) {\r\n for (const error of validateProfile.errors) {\r\n issues.push({\r\n code: \"PROFILE_VALIDATION\",\r\n path: `products[${i}]${error.instancePath || \"/\"}`,\r\n message: `Profile \"${profileId}\": ${error.message ?? \"erro de validacao\"}`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return { valid: issues.length === 0, issues };\r\n}\r\n\r\nfunction extractProducts(doc: Record<string, unknown>): Record<string, unknown>[] {\r\n if (doc.document_type === \"PRODUCT\" && doc.product && typeof doc.product === \"object\") {\r\n return [doc.product as Record<string, unknown>];\r\n }\r\n return [];\r\n}\r\n\r\nfunction extractXFields(obj: Record<string, unknown>): Record<string, unknown> {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n if (key.startsWith(\"x-\")) {\r\n result[key] = value;\r\n }\r\n }\r\n return result;\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/schema.ts","../src/validate.ts"],"sourcesContent":["export { schema, profiles, profileIds } from \"./schema.js\";\r\nexport { validate } from \"./validate.js\";\r\nexport type {\r\n ScalarValue,\r\n ImageType,\r\n Image,\r\n Measure,\r\n PhysicalDimensions,\r\n AttributeRef,\r\n AttributeValue,\r\n Option,\r\n LotPolicy,\r\n SalesUnit,\r\n SourceWhen,\r\n SuppliedMaterial,\r\n SuppliedMaterialCost,\r\n SuppliedMaterialQuantity,\r\n SuppliedMaterialSource,\r\n SupplyOutputEntry,\r\n Product,\r\n Predicate,\r\n Condition,\r\n Component,\r\n RuleOperation,\r\n Rule,\r\n Ruleset,\r\n LookupAxis,\r\n TableRow,\r\n Table,\r\n Dependency,\r\n Constraint,\r\n PriceList,\r\n Catalog,\r\n ProductRef,\r\n Context,\r\n Pricing,\r\n CatalogDocument,\r\n ProductDocument,\r\n PacpDocument,\r\n ProfileId,\r\n ValidationIssue,\r\n ValidationResult,\r\n} from \"./types.js\";\r\n","import { readFileSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\n\r\nfunction loadJson(relativePath: string): Record<string, unknown> {\r\n const fullPath = join(__dirname, relativePath);\r\n return JSON.parse(readFileSync(fullPath, \"utf8\"));\r\n}\r\n\r\nexport const schema = loadJson(\"./pacp.schema.json\") as Record<string, unknown>;\r\n\r\nexport const profiles = {\r\n moveis: loadJson(\"./profiles/moveis.schema.json\") as Record<string, unknown>,\r\n iluminacao: loadJson(\"./profiles/iluminacao.schema.json\") as Record<string, unknown>,\r\n \"pisos-revestimentos\": loadJson(\"./profiles/pisos-revestimentos.schema.json\") as Record<string, unknown>,\r\n \"fiscal-br\": loadJson(\"./profiles/fiscal-br.schema.json\") as Record<string, unknown>,\r\n} as const;\r\n\r\nexport type ProfileId = keyof typeof profiles;\r\n\r\nexport const profileIds = Object.keys(profiles) as ProfileId[];\r\n","import type { ValidationResult, ValidationIssue, PacpDocument } from \"./types.js\";\r\nimport { schema, profiles, type ProfileId } from \"./schema.js\";\r\n\r\nexport function validate(document: unknown): ValidationResult {\r\n let Ajv2020: typeof import(\"ajv/dist/2020\").default;\r\n let addFormats: typeof import(\"ajv-formats\").default;\r\n\r\n try {\r\n Ajv2020 = require(\"ajv/dist/2020\") as typeof import(\"ajv/dist/2020\").default;\r\n addFormats = require(\"ajv-formats\") as typeof import(\"ajv-formats\").default;\r\n } catch {\r\n throw new Error(\r\n 'Para usar validate(), instale ajv e ajv-formats: npm install ajv ajv-formats'\r\n );\r\n }\r\n\r\n const ajv = new Ajv2020({ allErrors: true, strict: false });\r\n addFormats(ajv);\r\n\r\n const issues: ValidationIssue[] = [];\r\n\r\n const validateSchema = ajv.compile(schema);\r\n const valid = validateSchema(document);\r\n\r\n if (!valid && validateSchema.errors) {\r\n for (const error of validateSchema.errors) {\r\n issues.push({\r\n code: \"SCHEMA\",\r\n path: error.instancePath || \"/\",\r\n message: error.message ?? \"Erro de validacao de schema\",\r\n });\r\n }\r\n }\r\n\r\n if (document && typeof document === \"object\" && !Array.isArray(document)) {\r\n const doc = document as Record<string, unknown>;\r\n const declaredProfiles = Array.isArray(doc.profiles) ? doc.profiles : [];\r\n\r\n for (const profileId of declaredProfiles) {\r\n if (typeof profileId !== \"string\") continue;\r\n\r\n const profileSchema = profiles[profileId as ProfileId];\r\n if (!profileSchema) {\r\n issues.push({\r\n code: \"UNKNOWN_PROFILE\",\r\n path: \"/profiles\",\r\n message: `Profile \"${profileId}\" nao e um profile oficial PACP`,\r\n });\r\n continue;\r\n }\r\n\r\n const products = extractProducts(doc);\r\n const validateProfile = ajv.compile(profileSchema);\r\n\r\n for (let i = 0; i < products.length; i++) {\r\n const xFields = extractXFields(products[i]);\r\n if (Object.keys(xFields).length === 0) continue;\r\n\r\n const profileValid = validateProfile(xFields);\r\n if (!profileValid && validateProfile.errors) {\r\n for (const error of validateProfile.errors) {\r\n issues.push({\r\n code: \"PROFILE_VALIDATION\",\r\n path: `products[${i}]${error.instancePath || \"/\"}`,\r\n message: `Profile \"${profileId}\": ${error.message ?? \"erro de validacao\"}`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return { valid: issues.length === 0, issues };\r\n}\r\n\r\nfunction extractProducts(doc: Record<string, unknown>): Record<string, unknown>[] {\r\n if (doc.document_type === \"PRODUCT\" && doc.product && typeof doc.product === \"object\") {\r\n return [doc.product as Record<string, unknown>];\r\n }\r\n return [];\r\n}\r\n\r\nfunction extractXFields(obj: Record<string, unknown>): Record<string, unknown> {\r\n const result: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n if (key.startsWith(\"x-\")) {\r\n result[key] = value;\r\n }\r\n }\r\n return result;\r\n}\r\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,136 +1,441 @@
1
1
  export { profileIds, profiles, schema } from './schema.cjs';
2
2
 
3
+ /**
4
+ * Valor escalar simples permitido em options, contexts, tabelas e regras.
5
+ * Sempre primitivo: string, number ou boolean (sem objetos ou arrays).
6
+ */
3
7
  type ScalarValue = string | number | boolean;
8
+ /**
9
+ * Tipo semântico de uma imagem. Consumidores podem usar para selecionar
10
+ * a imagem certa em cada contexto (vitrine, detalhe, ambientação, técnica).
11
+ */
4
12
  type ImageType = "MAIN" | "DETAIL" | "AMBIANCE" | "TECHNICAL" | "OTHER";
13
+ /**
14
+ * Referência a uma imagem de produto ou variante.
15
+ *
16
+ * Quando uma `option` possui `images`, consumidores DEVEM priorizá-las sobre
17
+ * `product.images` para exibição contextual daquela variante.
18
+ *
19
+ * Ver spec/latest/pacp.md §4.
20
+ */
5
21
  interface Image {
22
+ /** URI válida da imagem. */
6
23
  url: string;
24
+ /** Rótulo legível / legenda. */
7
25
  label?: string;
26
+ /** Texto alternativo descritivo (acessibilidade). */
8
27
  alt?: string;
28
+ /** Inteiro ≥ 0 para ordenação explícita. Quando ausente em todas as imagens, prevalece a ordem do array. */
9
29
  position?: number;
30
+ /** Tipo semântico. */
10
31
  type?: ImageType;
11
32
  }
33
+ /**
34
+ * Medida física com valor numérico e unidade.
35
+ *
36
+ * Exemplo: `{ value: 65, unit: "kg" }`.
37
+ */
12
38
  interface Measure {
39
+ /** Valor numérico positivo. */
13
40
  value: number;
41
+ /** Unidade SI ou comercial (ex: `kg`, `g`, `m`, `cm`). */
14
42
  unit: string;
15
43
  }
44
+ /**
45
+ * Dimensões físicas de um produto (largura × altura × profundidade).
46
+ *
47
+ * `unit` é obrigatório; cada dimensão individual é opcional.
48
+ * Exemplo: `{ width: 230, height: 95, depth: 100, unit: "cm" }`.
49
+ */
16
50
  interface PhysicalDimensions {
17
51
  width?: number;
18
52
  height?: number;
19
53
  depth?: number;
54
+ /** Unidade comum a todas as dimensões (ex: `cm`, `mm`, `m`). */
20
55
  unit: string;
21
56
  }
57
+ /**
58
+ * Declaração de um atributo configurável do produto (ex.: "cor", "tecido", "tamanho").
59
+ *
60
+ * Apenas declara o atributo; os valores selecionáveis vivem em `Option[]`
61
+ * referenciando esse `id` via `attribute_id`.
62
+ */
22
63
  interface AttributeRef {
64
+ /** ID estável do atributo, único por produto. */
23
65
  id: string;
66
+ /** Rótulo legível ao usuário final. */
24
67
  label?: string;
25
68
  }
69
+ /**
70
+ * Valor fixo de um atributo no nível do produto (não escolhível, informativo).
71
+ *
72
+ * Complementar a `Option[]` — use `AttributeValue` para atributos que NÃO variam
73
+ * no orçamento. Use `Option` para atributos selecionáveis. Ver spec §4.4.
74
+ */
26
75
  interface AttributeValue {
27
76
  attribute_id: string;
28
77
  value: ScalarValue;
29
78
  label?: string;
30
79
  }
80
+ /**
81
+ * Valor selecionável de um atributo do produto (ex.: "VELUDO" para `tecido`).
82
+ *
83
+ * O `id` é o handle PACP-interno (use em rules, dependencies, etc.);
84
+ * o `value` é o valor semântico do atributo (use em tabelas e `source_when`).
85
+ */
31
86
  interface Option {
87
+ /** ID estável da option, único por catálogo. */
32
88
  id: string;
89
+ /** Atributo ao qual esta option pertence. DEVE existir em `Product.attributes`. */
33
90
  attribute_id: string;
91
+ /** Valor semântico (string/number/boolean). Usado em lookups de tabelas e em `source_when`. */
34
92
  value: ScalarValue;
93
+ /** Rótulo legível ao usuário final. */
35
94
  label?: string;
95
+ /** Imagens contextuais da variante (priorizadas sobre `product.images`). */
36
96
  images?: Image[];
37
97
  }
98
+ /**
99
+ * Política de lote obrigatório no nível do produto. Ver spec §4.1.
100
+ *
101
+ * - `source: "CONTEXT"` → lote vem de `context[context_key]`.
102
+ * - `source: "ATTRIBUTE"` → lote vem da option selecionada do attribute.
103
+ *
104
+ * Quando `required=true`, ausência do lote bloqueia o cálculo.
105
+ */
38
106
  interface LotPolicy {
39
107
  required: boolean;
40
108
  source: "CONTEXT" | "ATTRIBUTE";
109
+ /** Obrigatório quando `source="CONTEXT"`. */
41
110
  context_key?: string;
111
+ /** Obrigatório quando `source="ATTRIBUTE"`. */
42
112
  attribute_id?: string;
43
113
  }
114
+ /**
115
+ * Política de conversão de unidade solicitada → unidade vendável. Ver spec §4.2 e §5.5.
116
+ *
117
+ * Exemplo: piso vendido em caixas de 2.5 m². Cliente pede 18 m² →
118
+ * `required_sell_units = CEIL(18 / 2.5) = 8 caixas`.
119
+ *
120
+ * Em PACP, `rounding` DEVE ser `CEIL`.
121
+ */
44
122
  interface SalesUnit {
123
+ /** Unidade do pedido (m², L, kg). DEVE ser igual a `Product.unit` quando ambos existem. */
45
124
  requested_unit: string;
125
+ /** Unidade comercial vendável (caixa, galão, saco). */
46
126
  sell_unit: string;
127
+ /** Quantidade da unidade solicitada que cabe em 1 unidade vendável. */
47
128
  quantity_per_sell_unit: number;
129
+ /** Em PACP DEVE ser `CEIL` (regra normativa). */
48
130
  rounding: "CEIL" | "FLOOR" | "ROUND" | "HALF_UP";
131
+ /** Piso mínimo de unidades vendáveis. */
49
132
  min_sell_units?: number;
50
133
  }
134
+ /**
135
+ * Quem fornece o material físico para a fábrica produzir o item.
136
+ *
137
+ * - `FACTORY`: fábrica fornece (preço somado via `factory_cost`).
138
+ * - `CUSTOMER`: cliente fornece (preço ignorado; sai em `supplied_quantities[]`).
139
+ */
140
+ type SuppliedMaterialSource = "FACTORY" | "CUSTOMER";
141
+ /** Quantidade fixa de insumo (use quando não varia por configuração). */
142
+ interface SuppliedMaterialQuantityValue {
143
+ /** Quantidade positiva. */
144
+ value: number;
145
+ /** Unidade (m², m, kg, un). */
146
+ unit: string;
147
+ }
148
+ /** Quantidade resolvida via lookup em tabela (use quando varia por configuração — ex: sofá 3 vs 4 lugares). */
149
+ interface SuppliedMaterialQuantityTable {
150
+ /** ID de uma tabela `LOOKUP` definida no catálogo. */
151
+ table_id: string;
152
+ /** Unidade do valor retornado pela tabela. */
153
+ unit: string;
154
+ }
155
+ /**
156
+ * Quantidade de insumo necessária por produto. Aceita valor fixo OU lookup.
157
+ *
158
+ * Sempre exige `unit`. Ver spec §4.8.
159
+ */
160
+ type SuppliedMaterialQuantity = SuppliedMaterialQuantityValue | SuppliedMaterialQuantityTable;
161
+ /** Custo do material declarado como valor literal. */
162
+ interface SuppliedMaterialCostValue {
163
+ /** Valor ≥ 0. Custo zero é permitido (material gratuito); negativo é inválido. */
164
+ value: number;
165
+ }
166
+ /** Custo do material resolvido via lookup em tabela. */
167
+ interface SuppliedMaterialCostTable {
168
+ table_id: string;
169
+ }
170
+ /** Custo do material resolvido executando um ruleset específico. */
171
+ interface SuppliedMaterialCostRuleset {
172
+ ruleset_id: string;
173
+ }
174
+ /**
175
+ * Custo do material quando fonte resolvida = `FACTORY`.
176
+ * Exatamente um de `value`, `table_id` ou `ruleset_id`.
177
+ *
178
+ * Ausente → engine NÃO soma (custo já incluso em `base_price` ou em rulesets externos).
179
+ */
180
+ type SuppliedMaterialCost = SuppliedMaterialCostValue | SuppliedMaterialCostTable | SuppliedMaterialCostRuleset;
181
+ /**
182
+ * Mapeia valores de `option.value` (do attribute referenciado por `sourcing_attribute_id`)
183
+ * para o modo de sourcing.
184
+ *
185
+ * Cada `value` distinto de option do attribute DEVE aparecer em `factory[]` OU `customer[]`;
186
+ * validadores reportam `UNCOVERED_OPTION_VALUE` caso contrário.
187
+ *
188
+ * Exemplo: `{ factory: ["FABRICA", "OWN"], customer: ["EU_FORNECO", "COB"] }`.
189
+ */
190
+ interface SourceWhen {
191
+ /** Valores que mapeiam para fonte = FACTORY. */
192
+ factory: ScalarValue[];
193
+ /** Valores que mapeiam para fonte = CUSTOMER. */
194
+ customer: ScalarValue[];
195
+ }
196
+ /**
197
+ * Insumo consumido pelo produto, com regra de quem fornece (fábrica ou cliente).
198
+ *
199
+ * Caso-tipo: sofá que aceita tecido próprio do lojista. Ver spec §4.8.
200
+ *
201
+ * Convenção dura: quando `supplied_materials` está presente, `base_price` representa
202
+ * o produto SEM os materiais declarados. Custo vive em `factory_cost`.
203
+ */
204
+ interface SuppliedMaterial {
205
+ /** ID único do insumo no produto. */
206
+ id: string;
207
+ /** Tipo do material em SNAKE_UPPER (ex: `TECIDO`, `COURO`, `VIDRO`, `MARMORE`). */
208
+ material: string;
209
+ /** Quantidade necessária. */
210
+ quantity: SuppliedMaterialQuantity;
211
+ /** Quem fornece quando não há escolha no orçamento. Default `FACTORY`. */
212
+ default_source?: SuppliedMaterialSource;
213
+ /** Attribute do produto cuja option selecionada decide a fonte. Quando presente, `source_when` é OBRIGATÓRIO. */
214
+ sourcing_attribute_id?: string;
215
+ /** Mapeia `option.value` para `FACTORY`/`CUSTOMER`. Obrigatório quando `sourcing_attribute_id` está presente. */
216
+ source_when?: SourceWhen;
217
+ /** Custo somado quando fonte resolvida = `FACTORY`. Ignorado quando = `CUSTOMER`. */
218
+ factory_cost?: SuppliedMaterialCost;
219
+ /** Bloco livre de requisitos do material. Profile `moveis` padroniza `x-fabric_requirements`. */
220
+ requirements?: Record<string, unknown>;
221
+ [key: `x-${string}`]: unknown;
222
+ }
223
+ /**
224
+ * Entrada no output do orçamento descrevendo material que o CLIENTE precisa fornecer.
225
+ *
226
+ * O engine produz uma entrada por cada `SuppliedMaterial` cuja fonte resolvida = `CUSTOMER`.
227
+ * PDV consome para exibir "fornecer X m² de tecido"; sistema de gestão gera pedido de fornecimento.
228
+ */
229
+ interface SupplyOutputEntry {
230
+ /** ID do `SuppliedMaterial` que originou esta entrada. */
231
+ material_id: string;
232
+ /** Tipo do material (`TECIDO`, `COURO`, etc.). */
233
+ material: string;
234
+ /** Quantidade resolvida pelo engine (após lookup, se aplicável). */
235
+ quantity: number;
236
+ /** Unidade da quantidade. */
237
+ unit: string;
238
+ /** Requisitos copiados do `SuppliedMaterial.requirements`. */
239
+ requirements?: Record<string, unknown>;
240
+ }
241
+ /**
242
+ * Produto único do catálogo. Ver spec §4.
243
+ *
244
+ * Campos descritivos (`sku`, `manufacturer`, `category`, etc.) são opcionais e não alteram cálculo.
245
+ * Configurabilidade vem de `attributes` + `options` + `rulesets` + `tables`.
246
+ *
247
+ * Estrutura típica:
248
+ * - `id`, `name`, `base_price`
249
+ * - `attributes: [{ id: "tecido" }, { id: "cor" }]`
250
+ * - `options: [{ id: "opt_veludo", attribute_id: "tecido", value: "VELUDO" }, ...]`
251
+ * - `ruleset_ids: ["rs_base"]`
252
+ */
51
253
  interface Product {
254
+ /** ID estável do produto, único por catálogo. Case-sensitive. */
52
255
  id: string;
256
+ /** Nome legível. */
53
257
  name?: string;
258
+ /**
259
+ * `PUBLIC` (default): exibível em vitrines.
260
+ * `INTERNAL`: existe no catálogo para orçamento mas não para vitrine pública.
261
+ * Caso típico INTERNAL: insumos, ferragens, componentes.
262
+ */
54
263
  visibility?: "PUBLIC" | "INTERNAL";
264
+ /** Código SKU para integração com ERP. */
55
265
  sku?: string;
56
266
  manufacturer?: string;
57
267
  brand?: string;
58
268
  description?: string;
269
+ /**
270
+ * Categorias hierárquicas. Cada path é um array da raiz à folha.
271
+ * Permite múltipla classificação. Exemplo: `[["Móveis", "Estofados", "Sofá"], ["Promoções"]]`.
272
+ */
59
273
  category?: string[][];
274
+ /** Código de barras GS1 (8-14 dígitos). */
60
275
  gtin?: string;
276
+ /**
277
+ * Preço base do produto. Ponto de partida dos rulesets de `BASE`.
278
+ * Quando `supplied_materials` está presente, representa o produto SEM os materiais declarados.
279
+ */
61
280
  base_price?: number;
281
+ /**
282
+ * Unidade base na qual `base_price` é cotado (ex: `un`, `m2`, `kg`). Default implícito: `un`.
283
+ * Quando coexistir com `sales_unit`, `sales_unit.requested_unit` DEVE ser igual a `unit`.
284
+ */
62
285
  unit?: string;
63
286
  images?: Image[];
287
+ /** Tags livres para busca. Sem garantia de estabilidade (diferente de `collections`). */
64
288
  tags?: string[];
289
+ /**
290
+ * IDs de coleções (agrupamento curatorial/sazonal estável).
291
+ * Disponível em rules como fato `product.collections` com operadores `IN`/`NOT_IN`.
292
+ * Exemplo: `["verao_2026", "linha_premium"]`. Ver spec §4.7.
293
+ */
65
294
  collections?: string[];
66
295
  weight?: Measure;
67
296
  dimensions?: PhysicalDimensions;
68
297
  lot_policy?: LotPolicy;
69
298
  sales_unit?: SalesUnit;
299
+ /** Atributos configuráveis do produto. */
70
300
  attributes?: AttributeRef[];
301
+ /** Valores fixos de atributos (não escolhíveis). Complementar a `options`. */
71
302
  attribute_values?: AttributeValue[];
303
+ /** Valores selecionáveis dos atributos. */
72
304
  options: Option[];
305
+ /** Insumos consumidos pelo produto, com sourcing factory/customer. Ver spec §4.8. */
306
+ supplied_materials?: SuppliedMaterial[];
307
+ /** IDs de rulesets aplicados a este produto. */
73
308
  ruleset_ids?: string[];
74
309
  [key: `x-${string}`]: unknown;
75
310
  }
311
+ /**
312
+ * Condição atômica em `Condition.all`/`Condition.any`.
313
+ *
314
+ * Fatos disponíveis incluem: `option.<id>`, `attribute.<id>`, `context.<key>`,
315
+ * `product.collections`, `product.category`, `supplied_materials.<id>.source`,
316
+ * `supplied_materials.<id>.quantity`, `supplied_materials.any.source`, `supplied_materials.all.source`.
317
+ */
76
318
  interface Predicate {
319
+ /** Fato a avaliar. */
77
320
  fact: string;
78
321
  operator: "EQ" | "NEQ" | "IN" | "NOT_IN" | "GT" | "GTE" | "LT" | "LTE" | "EXISTS";
322
+ /** Use para operadores escalares (`EQ`, `NEQ`, `GT`, etc.). */
79
323
  value?: ScalarValue;
324
+ /** Use para operadores de conjunto (`IN`, `NOT_IN`). */
80
325
  values?: ScalarValue[];
81
326
  }
327
+ /**
328
+ * Condição lógica composta. `all` = AND; `any` = OR.
329
+ * Pelo menos um dos campos é obrigatório.
330
+ */
82
331
  interface Condition {
83
332
  all?: Predicate[];
84
333
  any?: Predicate[];
85
334
  }
335
+ /** Componente de operações `MAX_OF`/`MIN_OF`/`PICK`. */
86
336
  interface Component {
87
337
  label?: string;
88
338
  value?: number;
89
339
  table_id?: string;
90
340
  option_id?: string;
91
341
  }
342
+ /**
343
+ * Operação executada por uma regra. Ver spec §6.
344
+ *
345
+ * - `ADD` / `PERCENT_OF`: acumulam.
346
+ * - `OVERRIDE` / `PICK`: substituem.
347
+ * - `LOOKUP`: busca em tabela.
348
+ * - `MAX_OF` / `MIN_OF`: agregam componentes.
349
+ * - `ROUND` / `CAP` / `FLOOR`: pós-processamento.
350
+ */
92
351
  type RuleOperation = "ADD" | "PERCENT_OF" | "OVERRIDE" | "LOOKUP" | "MAX_OF" | "MIN_OF" | "PICK" | "ROUND" | "CAP" | "FLOOR";
352
+ /**
353
+ * Regra de precificação aplicada dentro de um ruleset.
354
+ *
355
+ * Ordem de execução: por `priority` decrescente, desempate por `id` lexicográfico.
356
+ * Default: `priority=0`, `enabled=true`, `when` sempre verdadeiro.
357
+ */
93
358
  interface Rule {
359
+ /** ID único por ruleset. */
94
360
  id: string;
95
361
  operation: RuleOperation;
362
+ /** Maior primeiro. Default 0. */
96
363
  priority?: number;
364
+ /** Default true. */
97
365
  enabled?: boolean;
366
+ /** Quando ausente, regra sempre dispara. */
98
367
  when?: Condition;
368
+ /** Para `ADD`/`OVERRIDE`. */
99
369
  value?: number;
370
+ /** Para `PERCENT_OF`. */
100
371
  percent?: number;
372
+ /** Para `LOOKUP`. */
101
373
  table_id?: string;
374
+ /** Para `MAX_OF`/`MIN_OF`/`PICK`. */
102
375
  components?: Component[];
376
+ /** Para `ROUND`. */
103
377
  precision?: number;
378
+ /** Para `CAP`. */
104
379
  max?: number;
380
+ /** Para `FLOOR`. */
105
381
  min?: number;
382
+ /** Para `LOOKUP` sem chave correspondente. */
106
383
  fallback?: number;
107
384
  option_id?: string;
108
385
  option_ids?: string[];
109
386
  [key: `x-${string}`]: unknown;
110
387
  }
388
+ /**
389
+ * Conjunto de regras aplicadas em um estágio (`target`) do cálculo.
390
+ *
391
+ * Estágios: `BASE` (preço base), `SUBTOTAL` (após base), `TOTAL` (final).
392
+ */
111
393
  interface Ruleset {
112
394
  id: string;
113
395
  target: "BASE" | "SUBTOTAL" | "TOTAL";
114
396
  rules: Rule[];
115
397
  [key: `x-${string}`]: unknown;
116
398
  }
399
+ /**
400
+ * Dimensão de uma tabela `LOOKUP`. Define como obter o valor da chave.
401
+ *
402
+ * - `ATTRIBUTE`: usa option selecionada do attribute.
403
+ * - `CONTEXT`: usa valor de `context[context_key]`.
404
+ * - `LITERAL`: usa valor fixo.
405
+ */
117
406
  interface LookupAxis {
407
+ /** Nome da chave usada nas `rows`. */
118
408
  key: string;
119
409
  source: "ATTRIBUTE" | "CONTEXT" | "LITERAL";
120
410
  attribute_id?: string;
121
411
  context_key?: string;
122
412
  literal?: ScalarValue;
123
413
  }
414
+ /** Linha de uma tabela `LOOKUP` (chave composta → valor numérico). */
124
415
  interface TableRow {
416
+ /** Mapa de chave: `{ "tecido": "VELUDO", "lugares": "3" }`. */
125
417
  key: Record<string, ScalarValue>;
418
+ /** Valor retornado quando todas as dimensões batem. */
126
419
  value: number;
127
420
  }
421
+ /**
422
+ * Tabela de lookup determinística usada por regras `LOOKUP` ou por `supplied_materials.quantity.table_id`.
423
+ *
424
+ * A chave de busca é construída pelas `dimensions` na ordem declarada.
425
+ */
128
426
  interface Table {
129
427
  id: string;
130
428
  type: "LOOKUP";
131
429
  dimensions: LookupAxis[];
132
430
  rows: TableRow[];
133
431
  }
432
+ /**
433
+ * Relação lógica entre opções. Avaliada na fase de validação (antes do cálculo).
434
+ *
435
+ * - `REQUIRES`: opção A exige opção B selecionada.
436
+ * - `IMPLIES`: seleção de A implica B.
437
+ * - `AVAILABLE_OPTIONS_WHEN`: lista opções habilitadas sob condição.
438
+ */
134
439
  interface Dependency {
135
440
  id: string;
136
441
  type: "REQUIRES" | "IMPLIES" | "AVAILABLE_OPTIONS_WHEN";
@@ -140,20 +445,33 @@ interface Dependency {
140
445
  allowed_option_ids?: string[];
141
446
  when?: Condition;
142
447
  }
448
+ /**
449
+ * Bloqueio duro de combinação. Quando `when` é verdadeiro, cálculo é interrompido.
450
+ *
451
+ * Avaliada antes do cálculo de preço (junto com dependencies).
452
+ */
143
453
  interface Constraint {
144
454
  id: string;
145
455
  type: "DENY";
146
456
  when: Condition;
457
+ /** Mensagem legível exibida ao orçamentista. */
147
458
  message: string;
148
459
  product_id?: string;
149
460
  option_ids?: string[];
150
461
  }
462
+ /**
463
+ * Lista de preço do catálogo.
464
+ *
465
+ * `context.price_list_id` seleciona qual lista usar; fallback para `catalog.default_price_list_id`.
466
+ */
151
467
  interface PriceList {
152
468
  id: string;
153
469
  currency: string;
154
470
  label?: string;
471
+ /** Casamento com `context` para seleção automática. */
155
472
  context_match?: Record<string, ScalarValue>;
156
473
  }
474
+ /** Metadados do catálogo (lista de preço, default). */
157
475
  interface Catalog {
158
476
  id: string;
159
477
  name?: string;
@@ -161,10 +479,18 @@ interface Catalog {
161
479
  price_lists?: PriceList[];
162
480
  [key: `x-${string}`]: unknown;
163
481
  }
482
+ /** Referência a um documento `PRODUCT` em arquivo separado, a partir do `CATALOG`. */
164
483
  interface ProductRef {
484
+ /** DEVE ser igual ao `product.id` do arquivo apontado. */
165
485
  id: string;
486
+ /** Caminho relativo ao diretório do manifesto. */
166
487
  path: string;
167
488
  }
489
+ /**
490
+ * Contexto de execução do orçamento. Carrega dados externos que rules e validações leem.
491
+ *
492
+ * Chaves arbitrárias `x-*` são permitidas para extensões customizadas.
493
+ */
168
494
  interface Context {
169
495
  price_list_id?: string;
170
496
  region?: string;
@@ -175,9 +501,16 @@ interface Context {
175
501
  requested_unit?: string;
176
502
  [key: string]: ScalarValue | undefined;
177
503
  }
504
+ /** Hint informativo sobre o modo de cálculo predominante do catálogo. */
178
505
  interface Pricing {
179
506
  calculation_mode?: "CASCADE" | "TABLE_LOOKUP" | "OVERRIDE_BY_VARIANT" | "COST_PLUS";
180
507
  }
508
+ /**
509
+ * Manifesto do catálogo. Contém metadados, listas de preço, rulesets globais,
510
+ * tabelas, dependencies, constraints e referências para arquivos `PRODUCT`.
511
+ *
512
+ * Identifica-se por `document_type: "CATALOG"`.
513
+ */
181
514
  interface CatalogDocument {
182
515
  document_type: "CATALOG";
183
516
  catalog: Catalog;
@@ -189,11 +522,18 @@ interface CatalogDocument {
189
522
  tables?: Table[];
190
523
  dependencies?: Dependency[];
191
524
  constraints?: Constraint[];
525
+ /** Extension profiles ativos (`moveis`, `iluminacao`, `pisos-revestimentos`, `fiscal-br`). */
192
526
  profiles?: string[];
193
527
  [key: `x-${string}`]: unknown;
194
528
  }
529
+ /**
530
+ * Documento isolado de um produto. Referenciado por um `CatalogDocument` via `product_refs[]`.
531
+ *
532
+ * Identifica-se por `document_type: "PRODUCT"`.
533
+ */
195
534
  interface ProductDocument {
196
535
  document_type: "PRODUCT";
536
+ /** ID do catálogo ao qual este produto pertence. DEVE bater com `catalog.id` no manifesto. */
197
537
  catalog_id: string;
198
538
  product: Product;
199
539
  rulesets?: Ruleset[];
@@ -203,13 +543,20 @@ interface ProductDocument {
203
543
  profiles?: string[];
204
544
  [key: `x-${string}`]: unknown;
205
545
  }
546
+ /** Qualquer documento PACP válido (CATALOG ou PRODUCT). */
206
547
  type PacpDocument = CatalogDocument | ProductDocument;
548
+ /** IDs dos profiles oficiais PACP. */
207
549
  type ProfileId = "moveis" | "iluminacao" | "pisos-revestimentos" | "fiscal-br";
550
+ /** Problema reportado pelo validador. */
208
551
  interface ValidationIssue {
552
+ /** Código machine-readable (ex: `SCHEMA`, `DUPLICATE_ID`, `MISSING_SOURCING_ATTRIBUTE`). */
209
553
  code: string;
554
+ /** JSON Pointer apontando para o local exato do problema. */
210
555
  path: string;
556
+ /** Mensagem humana descrevendo o problema. */
211
557
  message: string;
212
558
  }
559
+ /** Resultado da validação. `valid=true` ⇔ `issues.length === 0`. */
213
560
  interface ValidationResult {
214
561
  valid: boolean;
215
562
  issues: ValidationIssue[];
@@ -217,4 +564,4 @@ interface ValidationResult {
217
564
 
218
565
  declare function validate(document: unknown): ValidationResult;
219
566
 
220
- export { type AttributeRef, type AttributeValue, type Catalog, type CatalogDocument, type Component, type Condition, type Constraint, type Context, type Dependency, type Image, type ImageType, type LookupAxis, type LotPolicy, type Measure, type Option, type PacpDocument, type PhysicalDimensions, type Predicate, type PriceList, type Pricing, type Product, type ProductDocument, type ProductRef, type ProfileId, type Rule, type RuleOperation, type Ruleset, type SalesUnit, type ScalarValue, type Table, type TableRow, type ValidationIssue, type ValidationResult, validate };
567
+ export { type AttributeRef, type AttributeValue, type Catalog, type CatalogDocument, type Component, type Condition, type Constraint, type Context, type Dependency, type Image, type ImageType, type LookupAxis, type LotPolicy, type Measure, type Option, type PacpDocument, type PhysicalDimensions, type Predicate, type PriceList, type Pricing, type Product, type ProductDocument, type ProductRef, type ProfileId, type Rule, type RuleOperation, type Ruleset, type SalesUnit, type ScalarValue, type SourceWhen, type SuppliedMaterial, type SuppliedMaterialCost, type SuppliedMaterialQuantity, type SuppliedMaterialSource, type SupplyOutputEntry, type Table, type TableRow, type ValidationIssue, type ValidationResult, validate };