@pisapp/core 7.0.0-dev.20260916-153034

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.
Files changed (51) hide show
  1. package/LICENSE +28 -0
  2. package/THIRD-PARTY-NOTICES.txt +43 -0
  3. package/catalogs.d.ts +7725 -0
  4. package/catalogs.js +3 -0
  5. package/chunks/catalogs-domain-DJxK_B5E.js +9060 -0
  6. package/chunks/catalogs-domain-DJxK_B5E.js.map +1 -0
  7. package/chunks/classifications-domain-DdIJaF1D.js +1980 -0
  8. package/chunks/classifications-domain-DdIJaF1D.js.map +1 -0
  9. package/chunks/icons-shared-B8v5z3BE.js +1387 -0
  10. package/chunks/icons-shared-B8v5z3BE.js.map +1 -0
  11. package/chunks/products-domain-99vJaUyI.js +22587 -0
  12. package/chunks/products-domain-99vJaUyI.js.map +1 -0
  13. package/classifications.d.ts +7725 -0
  14. package/classifications.js +3 -0
  15. package/core.d.ts +7711 -0
  16. package/core.js +7 -0
  17. package/index.d.ts +7992 -0
  18. package/index.js +12 -0
  19. package/migration/API_V1_TO_V2_MIGRATION.md +1424 -0
  20. package/migration/legacy-option-rules.json +93 -0
  21. package/migration/v7.json +4101 -0
  22. package/migration/v7.schema.json +595 -0
  23. package/package.json +68 -0
  24. package/pis-csp.js +97 -0
  25. package/pis-csp.js.map +1 -0
  26. package/pis.js +105 -0
  27. package/pis.js.map +1 -0
  28. package/products.d.ts +7746 -0
  29. package/products.js +2 -0
  30. package/styles/fonts.css +1 -0
  31. package/styles/pis.css +1 -0
  32. package/styles/theme-abb.css +1 -0
  33. package/styles/theme-abb.optimized.css +1 -0
  34. package/styles/theme-cux2-blue-dark.css +1 -0
  35. package/styles/theme-cux2-blue-dark.optimized.css +1 -0
  36. package/styles/theme-cux2-blue-light.css +1 -0
  37. package/styles/theme-cux2-blue-light.optimized.css +1 -0
  38. package/styles/theme-cux2-lilac-dark.css +1 -0
  39. package/styles/theme-cux2-lilac-dark.optimized.css +1 -0
  40. package/styles/theme-cux2-lilac-light.css +1 -0
  41. package/styles/theme-cux2-lilac-light.optimized.css +1 -0
  42. package/styles/theme-cux2-mono-dark.css +1 -0
  43. package/styles/theme-cux2-mono-dark.optimized.css +1 -0
  44. package/styles/theme-cux2-mono-light.css +1 -0
  45. package/styles/theme-cux2-mono-light.optimized.css +1 -0
  46. package/styles/theme-cux2-red-dark.css +1 -0
  47. package/styles/theme-cux2-red-dark.optimized.css +1 -0
  48. package/styles/theme-cux2-red-light.css +1 -0
  49. package/styles/theme-cux2-red-light.optimized.css +1 -0
  50. package/types.d.ts +8893 -0
  51. package/types.js +0 -0
@@ -0,0 +1,1424 @@
1
+ # PIS API v1 to v2 Migration
2
+
3
+ ## Purpose
4
+
5
+ This is the living migration guide for breaking request-contract changes between PIS API v1 and v2.
6
+
7
+ Update this document in the same change set as every v2 contract change. For each change, record the affected endpoint or shared request model, v1 and v2 JSON shapes, compatibility behavior, validation, implementation status, and tests.
8
+
9
+ ## Versioning Policy
10
+
11
+ - v1 remains supported for existing consumers and its request contracts must not change.
12
+ - v2 uses explicit API-versioned actions and separate v2 request types where the request shape changes.
13
+ - A v2 response remains identical to v1 unless the change is explicitly documented here.
14
+ - Unknown legacy JSON properties are ignored by the current Newtonsoft.Json configuration. This supports phased client migration, but clients should stop sending removed properties.
15
+
16
+ ## Change Log
17
+
18
+ | Change | Status | First tracked |
19
+ | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------- |
20
+ | Replace v2 `AppSettings.Filters.PuId` with `InclusionCriteria` for `ProducingUnit` | Standard contract mapping implemented; application-profile sales-status logic remains query-composer behavior | 2026-08-25 |
21
+ | Remove `AppSettings.Filters.BuId` from v2 | Implemented | 2026-08-11 |
22
+ | Replace v2 `AppSettings.Filters.SuId` with `InclusionCriteria` rules for `LocSalOrg` fields | Contract mapping implemented; recursive v2 query compilation pending | 2026-08-25 |
23
+ | Represent v1 `AppSettings.Filters.SalesErp` as inclusion or exclusion criteria | Internal contract mapping implemented; recursive v2 query compilation pending | 2026-08-25 |
24
+ | Replace v2 `InclusionCriteria` list with a recursive `Criteria` tree | Contract and validation implemented; includes the target replacement shape for `BuId`; Solr query compilation pending | 2026-08-11 |
25
+ | Replace the combined v2 criteria DTO with `CriteriaGroup`/`Criterion` and rename search `filterSettings` to `filter` | Implemented | 2026-09-04 |
26
+ | Allow v2 `CriteriaGroup.rules` to be omitted, `null`, or empty | Implemented with focused validator tests; non-empty trees retain recursive validation | 2026-09-07 |
27
+ | Rename the criterion value type and OpenAPI schema from `CriteriaValue` to `CriterionValue` | Implemented; JSON value shape unchanged | 2026-09-04 |
28
+ | Add `Criteria.operator: HasValue` | Implemented for application inclusion/exclusion criteria and search filter settings | 2026-09-01 |
29
+ | Remove `AppSettings.AppSpecific.AssociatedGroups` and `FrontEndFilter` from v2 | Implemented with a dedicated `AppSpecificSettings` contract | 2026-09-01 |
30
+ | Move `AppSettings.AppSpecific.TableRowMatching` to `AppSettings.Filters.TableRowMatching` in v2 | Implemented with v1-to-internal-v2 conversion | 2026-09-01 |
31
+ | Move Detail `AppSettings.AppSpecific.RelatedProducts` to `DataTypes` in v2 | Implemented with v1 compatibility conversion in `DetailService` | 2026-09-01 |
32
+ | Move Search `AppSettings.AppSpecific.TreeDepth` to top-level `SearchQuery.TreeDepth` | Implemented with v1 compatibility handling in `SearchService` | 2026-09-02 |
33
+ | Rename `AppSettings.InternalUser` to `IncludeInternalData` in v2 | Implemented with v1-to-internal conversion | 2026-09-04 |
34
+ | Remove obsolete `InteractiveGuides` data types and response properties from v2 | Implemented in OpenAPI and runtime JSON | 2026-09-07 |
35
+ | Remove the `V2` suffix from v2 OpenAPI schema names | Implemented without collision exceptions | 2026-09-02 |
36
+ | Shorten v2 `Criterion` derived OpenAPI schema names | Implemented | 2026-09-04 |
37
+ | Make optional v2 boolean request properties nullable | Implemented with explicit runtime fallbacks; shared catalog alias setting also changes in v1 | 2026-09-02 |
38
+ | Replace Translation `Components` with enum-based `Domains` and response `Component` with `Domain` in v2 | Implemented | 2026-09-03 |
39
+ | Return criterion-ready values from v2 catalog and product FilterValues endpoints | Implemented; focused response-contract tests pending | 2026-09-07 |
40
+ | Split v2 product and relationship Filters metadata from FilterValues | Implemented; relationship v1 compatibility tests retained | 2026-09-11 |
41
+ | Split v2 catalog Filters metadata from FilterValues | Implemented | 2026-09-14 |
42
+ | Add filter metadata settings to v2 catalog product Search | Implemented | 2026-09-14 |
43
+ | Reduce v2 Search filter responses to metadata and add optional filter limiting | Implemented | 2026-09-11 |
44
+ | Require at least one v2 Search `dataTypes` value | Implemented | 2026-09-11 |
45
+ | Add optional `IncludeRawValues` to v2 product queries | Implemented; focused response-contract tests pending | 2026-09-09 |
46
+ | Add v2 sitemap products endpoint | Implemented for `AppSettings` and flat scalar criteria | 2026-08-11 |
47
+ | Remove properties marked `[Obsolete]` from v2 JSON responses | Implemented with version-aware System.Text.Json metadata | 2026-09-09 |
48
+ | Load v2 product variant table groups incrementally | Implemented; v2 Detail returns sparse groups and `Variants/Table` loads one full group | 2026-09-16 |
49
+ | Remove `AppSettings.Client` from product v2 requests | Implemented; use standard HTTP headers and connection metadata | 2026-09-16 |
50
+
51
+ ## Product AppSettings.Client
52
+
53
+ Product v2 requests no longer expose `appSettings.client`. Client connection metadata belongs to the HTTP request rather than application settings. This change applies to requests that use the product `AppSettingsV2` contract. Catalog v2 requests currently share `CatalogAppSettings` with v1 and are not changed by this migration item.
54
+
55
+ ### v1
56
+
57
+ V1 continues to accept client metadata in the request body for compatibility:
58
+
59
+ ```json
60
+ {
61
+ "appSettings": {
62
+ "appCode": "example-app",
63
+ "client": {
64
+ "ipAddress": "192.0.2.1",
65
+ "userAgent": "Mozilla/5.0 ..."
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ Request headers take precedence over these legacy body values.
72
+
73
+ ### v2
74
+
75
+ Remove `client` from `appSettings`:
76
+
77
+ ```json
78
+ {
79
+ "appSettings": {
80
+ "appCode": "example-app"
81
+ }
82
+ }
83
+ ```
84
+
85
+ Use the existing HTTP headers instead:
86
+
87
+ ```http
88
+ Component-Version: 4.12.0
89
+ User-Agent: Mozilla/5.0 ...
90
+ X-Forwarded-For: 192.0.2.1
91
+ ```
92
+
93
+ - `Component-Version` remains unchanged and identifies the PIS component version.
94
+ - `User-Agent` supplies browser or caller information used for analytics, device and crawler detection, and client rate limiting.
95
+ - `X-Forwarded-For` supplies the end-user IP when a backend or reverse proxy calls PIS on the user's behalf.
96
+ - For a browser calling PIS directly, no IP header is required; PIS uses the connection `RemoteIpAddress`.
97
+ - For a backend-to-backend request without an end user, omit `X-Forwarded-For`; the backend connection address is used.
98
+
99
+ The original client address must be first when a controlled proxy chain contains multiple addresses:
100
+
101
+ ```http
102
+ X-Forwarded-For: 192.0.2.1, 198.51.100.10
103
+ ```
104
+
105
+ ### Rate Limiting and Proxy Requirements
106
+
107
+ PIS applies both IP and client rate limiting. A backend or reverse proxy that does not forward the end-user IP and `User-Agent` causes its users to share the backend's rate-limit identities and may produce `429 TooManyRequests` responses.
108
+
109
+ Only a server-side caller that obtained the address from the incoming connection should set `X-Forwarded-For`. An internet-facing proxy must overwrite an untrusted incoming value with the address that connected to it. It must not blindly append to a caller-provided value, because callers can forge HTTP headers and evade IP-based limits.
110
+
111
+ ### Compatibility and Tests
112
+
113
+ - V1 body fallback remains supported.
114
+ - Product v2 ignores legacy `appSettings.client` for request metadata; unknown JSON properties remain tolerated during migration.
115
+ - Existing `Component-Version`, `User-Agent`, and `X-Forwarded-For` names are retained; no new PIS-prefixed alternatives are introduced.
116
+ - Focused middleware tests cover header extraction, IP-chain deduplication, direct connection fallback, v1 body compatibility, and product v2 body removal.
117
+
118
+ ## Obsolete Response Properties
119
+
120
+ Properties marked with `[Obsolete]` remain serialized by v1 endpoints for backward compatibility. V2 endpoints omit those properties from JSON responses, matching their removal from the v2 OpenAPI document. This applies recursively to shared response DTOs, including `ProductAttribute.IsInternal`; its replacement is `ProductAttribute.Internal`.
121
+
122
+ ## Product Attribute Raw Values
123
+
124
+ The v2 product Autocomplete, Detail, List, Preview, Relationship Autocomplete, Search, and Sitemap Products requests
125
+ accept the optional top-level `includeRawValues` property:
126
+
127
+ ```json
128
+ {
129
+ "includeRawValues": true,
130
+ "appSettings": {
131
+ "context": {
132
+ "localization": {
133
+ "language": "en"
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ When `includeRawValues` is `true`, each returned product `AttributeValue` includes a criterion-ready `rawValue`.
141
+ Clients can pass this object directly as an item in `AttributeCriterion.values`. For scalar and enumeration
142
+ attributes, `rawValue.V` contains the source value or enumeration code. For multidimensional attributes, the object
143
+ also preserves the applicable header in `H` and unit of measure in `U`:
144
+
145
+ ```json
146
+ {
147
+ "text": "50 mm",
148
+ "rawValue": {
149
+ "H": "Width",
150
+ "V": "50",
151
+ "U": "mm"
152
+ }
153
+ }
154
+ ```
155
+
156
+ Omitting `includeRawValues`, or sending `null` or `false`, leaves `rawValue` unset. The option changes only the
157
+ attribute-value response data; it does not affect product matching, attribute selection, or formatted display text.
158
+ The v1 request contracts are unchanged.
159
+
160
+ ## Optional CriteriaGroup Rules
161
+
162
+ The shared v2 `CriteriaGroup` request model accepts `rules` when it is omitted, explicitly `null`, or an empty array. This permits clients to send an inactive filter group such as `{"type":"group","condition":"And","rules":[]}`. Non-empty groups continue to validate every nested criterion recursively. This only relaxes v2 request validation; v1 request contracts are unchanged.
163
+
164
+ ## Optional Boolean Request Properties
165
+
166
+ Optional boolean request properties in v2 use nullable JSON values. For the properties listed below, omitting the property or sending `null` leaves the option unspecified. The service layer resolves an unspecified value to `false`, preserving the previous behavior:
167
+
168
+ - `SearchQuery.includeProductClassifications`, `SearchQuery.includeExtendedObjectData`
169
+ - `ListQuery.includeProductClassifications`
170
+ - `AutocompleteQuery.categorySearch`, `categoryKeywordSearch`, `autoCategoryNavigation`, `fetchProductImages`, `fetchCategoryImages`
171
+ - `PrmAutocompleteQuery.fetchProductImages`
172
+ - `AutocompleteQuery.includeRawValues`, `DetailQuery.includeRawValues`, `ListQuery.includeRawValues`, `PreviewQuery.includeRawValues`
173
+ - `PrmAutocompleteQuery.includeRawValues`, `SearchQuery.includeRawValues`, `SitemapProductSearchQuery.includeRawValues`
174
+ - `CatalogProductAutocompleteQuery.fetchProductImages`
175
+ - `AppSettings.filters.tableRowMatching`
176
+ - `AppSettings.appSpecific.genericizeAttName`, `allReplacements`
177
+ - `CompareSearchSettings.differencesOnly`, `specialDocumentHandling`
178
+ - `ClassificationSearchSettings.configurators`
179
+ - `CatalogAppSettings.appSpecific.configuredItemAliasesEnabled`
180
+
181
+ `CompareSearchSettings.strictMode` is also nullable, but retains its documented default of `true`. Omitting it or sending `null` enables strict mode; clients must send `false` to disable it.
182
+
183
+ The OpenAPI v2 schema represents the false-fallback properties with `nullable: true` and `default: null`. It represents `strictMode` with `nullable: true` and `default: true`.
184
+
185
+ The Compare and classification settings use dedicated v2 request types, so their v1 property types remain non-nullable. `CatalogAppSpecificSettings` remains shared by both API versions and is changed directly; consequently, `configuredItemAliasesEnabled` is nullable in both v1 and v2.
186
+
187
+ ## AppSettings.InternalUser
188
+
189
+ ### v1
190
+
191
+ The v1 `AppSettings` contract uses `internalUser` to request internal-only data:
192
+
193
+ ```json
194
+ {
195
+ "appSettings": {
196
+ "internalUser": true
197
+ }
198
+ }
199
+ ```
200
+
201
+ ### v2
202
+
203
+ Rename the property to `includeInternalData`:
204
+
205
+ ```json
206
+ {
207
+ "appSettings": {
208
+ "includeInternalData": true
209
+ }
210
+ }
211
+ ```
212
+
213
+ The new name describes the requested response and filtering behavior instead of identifying the caller. The property remains nullable: omitting it, sending `null`, or sending `false` does not enable internal data. Application-level authorization is still required when the value is `true`.
214
+
215
+ ### Compatibility and Validation
216
+
217
+ - The v1 request contract and `internalUser` behavior are unchanged.
218
+ - During v1 conversion, `internalUser` is copied to `AppSettingsInternal.IncludeInternalData`.
219
+ - V2 requests use `includeInternalData`, which is copied to the same internal execution property.
220
+ - The legacy `internalUser` property is absent from the v2 OpenAPI schema. If sent in a v2 payload, it is treated as an unknown property and ignored by the current Newtonsoft.Json configuration.
221
+ - Responses are unchanged.
222
+
223
+ ## ApplicationFilters.PuId
224
+
225
+ ### v1
226
+
227
+ ```json
228
+ {
229
+ "appSettings": {
230
+ "filters": {
231
+ "puId": ["FIDRI", "SEMOT"]
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ `AppSettings.Filters.PuId` is a list of Producing Unit codes. The standard v1 query composer removes empty values and filters the `ProducingUnit` attribute, combining supplied values with `OR`.
238
+
239
+ ### v2
240
+
241
+ Replace `appSettings.filters.puId` with one `ProducingUnit` rule in `appSettings.filters.inclusionCriteria`:
242
+
243
+ ```json
244
+ {
245
+ "appSettings": {
246
+ "filters": {
247
+ "inclusionCriteria": {
248
+ "condition": "And",
249
+ "rules": [
250
+ {
251
+ "type": "attribute",
252
+ "code": "ProducingUnit",
253
+ "values": [{ "V": "FIDRI" }, { "V": "SEMOT" }],
254
+ "operator": "In"
255
+ }
256
+ ]
257
+ }
258
+ }
259
+ }
260
+ }
261
+ ```
262
+
263
+ The values of one `ProducingUnit` rule are alternatives, so this means `ProducingUnit = FIDRI OR ProducingUnit = SEMOT`. Place the rule in the root `And` group alongside any other inclusion rules.
264
+
265
+ ### Application Profile Sales-Status Logic
266
+
267
+ When the current application's `ApplicationProfile` enables `ExtraPuIdProductSalesStatusLogic`, clients still send only the `ProducingUnit` criterion shown above. During query compilation, PIS automatically expands every `ProducingUnit` value with an `OR` alternative for `ProductSalesStatus_PU{producingUnit}` equal to `A`, `O`, or `P`.
268
+
269
+ For example, with `FIDRI`, PIS applies `ProducingUnit = FIDRI OR ProductSalesStatus_PUFIDRI = A|O|P`. Do not add `ProductSalesStatus_PU...` rules to the v2 request; the application profile controls whether the expansion is active.
270
+
271
+ ### Compatibility and Validation
272
+
273
+ - The v1-to-internal-v2 conversion removes empty `puId` entries and adds one `ProducingUnit` rule to `inclusionCriteria` when values remain.
274
+ - The `ExtraPuIdProductSalesStatusLogic` application-profile flag changes query composition to also match `ProductSalesStatus_PU{puId}` values `A`, `O`, and `P`. The `ProducingUnit` criteria compiler applies this profile-dependent behavior, preserving the v1 result set.
275
+ - The standard `ProducingUnit` replacement is a flat scalar criterion supported by `POST /v2/Sitemap/Products`.
276
+ - Other product endpoints do not execute `Criteria` yet.
277
+
278
+ ## ApplicationFilters.BuId
279
+
280
+ ### v1
281
+
282
+ ```json
283
+ {
284
+ "appSettings": {
285
+ "filters": {
286
+ "buId": ["3127", "3119", "3129"]
287
+ }
288
+ }
289
+ }
290
+ ```
291
+
292
+ `AppSettings.Filters.BuId` is a `List<string>` and limits results by ABB Detail Group.
293
+
294
+ The current v1 query composer creates one filter for the attribute field `BuId` and combines the supplied values with `OR`. For example, `["3127", "3119", "3129"]` means: product `BuId` is `3127` OR product `BuId` is `3119` OR product `BuId` is `3129`.
295
+
296
+ ### v2
297
+
298
+ `appSettings.filters.buId` is removed from `ApplicationFilters`.
299
+
300
+ The v2 replacement is `appSettings.filters.inclusionCriteria` with `code` set to `BuId`:
301
+
302
+ ```json
303
+ {
304
+ "appSettings": {
305
+ "filters": {
306
+ "inclusionCriteria": {
307
+ "condition": "And",
308
+ "rules": [
309
+ {
310
+ "type": "attribute",
311
+ "code": "BuId",
312
+ "values": [{ "V": "3127" }, { "V": "3119" }, { "V": "3129" }],
313
+ "operator": "In"
314
+ }
315
+ ]
316
+ }
317
+ }
318
+ }
319
+ }
320
+ ```
321
+
322
+ This is a single `Criteria` group, not an array. The group contains one `BuId` rule whose values have the expected semantics `BuId = 3127 OR BuId = 3119 OR BuId = 3129`, matching v1.
323
+
324
+ Do not send `H` or `U` for `BuId`. Put each ABB Detail Group code directly in `values[].V`.
325
+
326
+ ### Client Migration
327
+
328
+ 1. Stop sending `appSettings.filters.buId` to v2 endpoints.
329
+ 2. Use one `inclusionCriteria` group with `condition: "And"` and one `BuId` rule in `rules`.
330
+ 3. Copy every v1 `buId` list item to a separate `rules[0].values[].V` entry.
331
+ 4. Use the replacement in `POST /v2/Sitemap/Products`; other product endpoints do not execute `Criteria` yet.
332
+ 5. Send the remaining supported v2 filter properties unchanged until their own migration entry says otherwise.
333
+
334
+ ### Compatibility and Validation
335
+
336
+ - v1 behavior is unchanged.
337
+ - A v2 payload containing legacy `buId` is currently accepted and the unknown property is ignored.
338
+ - `BuId` is not present in the v2 OpenAPI schema and is not validated by the v2 application-settings validator.
339
+
340
+ ## ApplicationFilters.SuId
341
+
342
+ ### v1
343
+
344
+ ```json
345
+ {
346
+ "appSettings": {
347
+ "filters": {
348
+ "suId": ["SUDESTO", "SUNOCRL"]
349
+ }
350
+ }
351
+ }
352
+ ```
353
+
354
+ `AppSettings.Filters.SuId` identifies sales units. The v1 query composer discards empty values and values shorter than three characters. For each remaining value it queries the attribute field `LocSalOrg` followed by the value starting at index `2`, and compares that field with the complete `SuId` value. Multiple `SuId` filters are combined with `OR`.
355
+
356
+ ### v2
357
+
358
+ Apply the same logic with an `Or` group in `appSettings.filters.inclusionCriteria`:
359
+
360
+ ```json
361
+ {
362
+ "appSettings": {
363
+ "filters": {
364
+ "inclusionCriteria": {
365
+ "condition": "And",
366
+ "rules": [
367
+ {
368
+ "type": "group",
369
+ "condition": "Or",
370
+ "rules": [
371
+ {
372
+ "type": "attribute",
373
+ "code": "LocSalOrgDESTO",
374
+ "values": [{ "V": "SUDESTO" }],
375
+ "operator": "In"
376
+ },
377
+ {
378
+ "type": "attribute",
379
+ "code": "LocSalOrgNOCRL",
380
+ "values": [{ "V": "SUNOCRL" }],
381
+ "operator": "In"
382
+ }
383
+ ]
384
+ }
385
+ ]
386
+ }
387
+ }
388
+ }
389
+ }
390
+ ```
391
+
392
+ For every valid `suId`, create one rule with `code` equal to `LocSalOrg` plus `suId.substring(2)`, and place the complete `suId` in `values[0].value`. Put all generated rules in one `Or` group. Place that group in the root `And` group alongside any other inclusion rules, preserving the v1 behavior.
393
+
394
+ ### Compatibility and Validation
395
+
396
+ - When a v1 `AppSettings` request is converted to internal v2 criteria, `SuId` is mapped to the nested `Or` group described above.
397
+ - Values that are empty after cleanup or shorter than three characters do not produce a criterion, matching the v1 query composer.
398
+ - `POST /v2/Sitemap/Products` currently rejects nested `rules`; therefore it cannot execute the multi-`SuId` v2 shape until recursive query compilation is implemented.
399
+ - Other product endpoints do not execute `Criteria` yet.
400
+
401
+ ## ApplicationFilters.SalesErp
402
+
403
+ ### v1
404
+
405
+ ````json
406
+ {
407
+ "appSettings": {
408
+ "filters": {
409
+ "salesErp": true
410
+ }
411
+ }
412
+ ### v2
413
+
414
+ `ApplicationFilters.SalesErp` remains supported and has the same boolean behavior. The equivalent criteria representation used for the internal v1-to-v2 conversion is:
415
+
416
+ ```json
417
+ {
418
+ "appSettings": {
419
+ "filters": {
420
+ "inclusionCriteria": {
421
+ "condition": "And",
422
+ "rules": [
423
+ {
424
+ "type": "attribute",
425
+ "code": "SalesErp",
426
+ "values": [{ "V": "Y" }],
427
+ "operator": "In"
428
+ }
429
+ ]
430
+ }
431
+ }
432
+ }
433
+ }
434
+ ````
435
+
436
+ For `salesErp: false`, use the same rule in `exclusionCriteria` instead:
437
+
438
+ ```json
439
+ {
440
+ "appSettings": {
441
+ "filters": {
442
+ "exclusionCriteria": {
443
+ "condition": "And",
444
+ "rules": [
445
+ {
446
+ "type": "attribute",
447
+ "code": "SalesErp",
448
+ "values": [{ "V": "Y" }],
449
+ "operator": "In"
450
+ }
451
+ ]
452
+ }
453
+ }
454
+ }
455
+ }
456
+ ```
457
+
458
+ ### Compatibility and Validation
459
+
460
+ - The v1-to-internal-v2 conversion adds `SalesErp = "Y"` to `inclusionCriteria` for `true` and to `exclusionCriteria` for `false`.
461
+ - `salesErp: null` does not add a criterion.
462
+ - `ApplicationFilters.SalesErp` remains available for v2 clients and is passed through unchanged.
463
+ - `POST /v2/Sitemap/Products` supports only flat scalar criteria; both examples above are flat scalar groups, although recursive query composition remains pending for other endpoints.
464
+
465
+ ## ApplicationFilters.InclusionCriteria
466
+
467
+ ### v1
468
+
469
+ ```json
470
+ {
471
+ "appSettings": {
472
+ "filters": {
473
+ "inclusionCriteria": [
474
+ {
475
+ "code": "Color",
476
+ "values": ["Red", "Blue"]
477
+ },
478
+ {
479
+ "code": "Voltage",
480
+ "values": ["230"]
481
+ }
482
+ ]
483
+ }
484
+ }
485
+ }
486
+ ```
487
+
488
+ ```csharp
489
+ public List<Criteria>? InclusionCriteria { get; set; }
490
+ ```
491
+
492
+ ### v2
493
+
494
+ ```json
495
+ {
496
+ "appSettings": {
497
+ "filters": {
498
+ "inclusionCriteria": {
499
+ "condition": "And",
500
+ "rules": [
501
+ {
502
+ "type": "attribute",
503
+ "code": "Color",
504
+ "values": [{ "V": "Red" }, { "V": "Blue" }],
505
+ "operator": "In"
506
+ },
507
+ {
508
+ "type": "attribute",
509
+ "code": "Voltage",
510
+ "values": [
511
+ { "V": "230", "U": "V" },
512
+ { "V": "400", "U": "V" }
513
+ ],
514
+ "operator": "In"
515
+ }
516
+ ]
517
+ }
518
+ }
519
+ }
520
+ }
521
+ ```
522
+
523
+ ```csharp
524
+ public CriteriaGroup? InclusionCriteria { get; set; }
525
+ ```
526
+
527
+ The root is always a `CriteriaGroup` with `condition` and `rules`. Every item in `rules` is a polymorphic `Criterion`
528
+ and requires `type: "attribute"` or `type: "group"`. An `AttributeCriterion` has `code`, optional `values`, and
529
+ `operator`; a nested `CriteriaGroup` has its own `condition` and `rules`. The root does not require `type` because its
530
+ contract type is already `CriteriaGroup`.
531
+
532
+ Each item in `AttributeCriterion.values` uses the `CriterionValue` .NET type and OpenAPI schema. This replaces the
533
+ previous `CriteriaValue` name so that the value is associated with one criterion. Its JSON representation remains
534
+ unchanged and contains required `V` plus optional `H` and `U` properties.
535
+
536
+ ### Replacing BuId with InclusionCriteria
537
+
538
+ To migrate this v1 request:
539
+
540
+ ```json
541
+ {
542
+ "appSettings": {
543
+ "filters": {
544
+ "buId": ["3127", "3119", "3129"]
545
+ }
546
+ }
547
+ }
548
+ ```
549
+
550
+ send this v2 request shape instead:
551
+
552
+ ```json
553
+ {
554
+ "appSettings": {
555
+ "filters": {
556
+ "inclusionCriteria": {
557
+ "condition": "And",
558
+ "rules": [
559
+ {
560
+ "type": "attribute",
561
+ "code": "BuId",
562
+ "values": [{ "V": "3127" }, { "V": "3119" }, { "V": "3129" }],
563
+ "operator": "In"
564
+ }
565
+ ]
566
+ }
567
+ }
568
+ }
569
+ }
570
+ ```
571
+
572
+ The compiler for a product endpoint must translate the nested `BuId` rule to one exact attribute filter on `BuId` and OR its values. It must not require `H` or `U`, and it must not translate the codes as display labels.
573
+
574
+ The criteria model contains:
575
+
576
+ | Property | Requirement in v2 | Notes |
577
+ | ------------ | ---------------------------------------------------------- | ------------------------------------------------------------------- |
578
+ | `type` | Required for every item in `rules`: `attribute` or `group` | Not required on the root `CriteriaGroup`. |
579
+ | `condition` | Required for a `CriteriaGroup`: `And` or `Or` | Combines the group's nested `rules`. |
580
+ | `code` | Required for an `AttributeCriterion`, non-empty | Attribute code or `CID`. |
581
+ | `values` | Required and non-empty for `In`; omitted for `HasValue` | Every `In` item requires a non-empty `V`. |
582
+ | `values[].H` | Optional | Used for multidimensional values. |
583
+ | `values[].U` | Optional | Used for multidimensional values. |
584
+ | `rules` | Required for a `CriteriaGroup`, non-empty recursive list | Every nested item is an `AttributeCriterion` or `CriteriaGroup`. |
585
+ | `operator` | Required for an `AttributeCriterion`: `In` or `HasValue` | `In` matches one of `values`; `HasValue` matches any indexed value. |
586
+
587
+ ### HasValue Operator
588
+
589
+ Use `HasValue` when only the presence of an indexed attribute value matters:
590
+
591
+ ```json
592
+ {
593
+ "condition": "And",
594
+ "rules": [
595
+ {
596
+ "type": "attribute",
597
+ "code": "BimRelevance",
598
+ "operator": "HasValue"
599
+ }
600
+ ]
601
+ }
602
+ ```
603
+
604
+ Do not send `values` for this operator. The validator rejects a non-empty `values` collection because its contents
605
+ would be ignored. Query composition emits a Solr field-existence filter (`field:*`); exclusion criteria negate that
606
+ filter. For translated attributes, PIS checks the active language, English fallback, and country-specific language
607
+ fields. The v1 request contract is unchanged and does not expose this operator. Responses and cache behavior are
608
+ unchanged.
609
+
610
+ ### Client Migration
611
+
612
+ 1. Replace the v1 array with one root `CriteriaGroup`.
613
+ 2. Set the group's `condition` and put each attribute criterion in `rules`.
614
+ 3. Add `type: "attribute"` to attribute criteria and `type: "group"` to nested groups.
615
+ 4. Convert each v1 string value to `{ "V": "..." }` in the matching rule's `values`.
616
+ 5. For a `BuId` migration, use a `BuId` rule and preserve the v1 list as OR alternatives in that rule's `values`.
617
+ 6. Use `H`, `V`, and `U` for multidimensional filter values instead of exposing the internal index separator.
618
+
619
+ ### Search Filter Property Rename
620
+
621
+ The v2 search contracts for product search, PRM search, product preview, and catalog product search replace
622
+ `filterSettings` with `filter`:
623
+
624
+ ```csharp
625
+ public CriteriaGroup? Filter { get; set; }
626
+ ```
627
+
628
+ This is a breaking request-contract change. Clients must stop sending `search.filterSettings`; unknown legacy
629
+ properties are ignored and therefore do not filter results. The v1 contracts retain their existing
630
+ `List<FilterSettings>? FilterSettings` property.
631
+
632
+ ### Current Implementation Status
633
+
634
+ - `ApplicationFilters.InclusionCriteria` and `ExclusionCriteria` use `CriteriaGroup`.
635
+ - Recursive groups are represented by `CriteriaGroup`; leaves are represented by `AttributeCriterion` through the polymorphic `Criterion` base type.
636
+ - Recursive structure and required values are validated by `CriteriaGroupValidator` and `AttributeCriterionValidator`.
637
+ - Product query composition supports recursive inclusion/exclusion groups, `In`, `HasValue`, multidimensional values, and `CID` criteria.
638
+ - Search `filter` processing preserves the existing filter-settings behavior: direct attribute rules are combined with AGM predefined filters and applied to product queries.
639
+ - The v2 `Translations` endpoint accepts `AppSettings`, but it does not apply product filters. Its availability does not imply that v2 search filtering is available.
640
+
641
+ ## AppSettings.AppSpecific
642
+
643
+ `AppSettings.AppSpecific` uses the dedicated `AppSpecificSettings` contract. The `associatedGroups` and
644
+ `frontEndFilter` properties are removed from this object. In v2, apply them through attribute criteria in
645
+ `appSettings.filters`.
646
+
647
+ ### AssociatedGroups
648
+
649
+ #### v1
650
+
651
+ `AppSettings.AppSpecific.AssociatedGroups` restricts results to products belonging to one of the specified associated
652
+ groups:
653
+
654
+ ```json
655
+ {
656
+ "appSettings": {
657
+ "appSpecific": {
658
+ "associatedGroups": ["0229/0x Platform63", "0229/1x Platform63"]
659
+ }
660
+ }
661
+ }
662
+ ```
663
+
664
+ #### v2
665
+
666
+ Move the non-empty values to one `AssociatedGroups` rule in `appSettings.filters.inclusionCriteria`:
667
+
668
+ ```json
669
+ {
670
+ "appSettings": {
671
+ "filters": {
672
+ "inclusionCriteria": {
673
+ "condition": "And",
674
+ "rules": [
675
+ {
676
+ "type": "attribute",
677
+ "code": "AssociatedGroups",
678
+ "values": [{ "V": "0229/0x Platform63" }, { "V": "0229/1x Platform63" }],
679
+ "operator": "In"
680
+ }
681
+ ]
682
+ }
683
+ }
684
+ }
685
+ }
686
+ ```
687
+
688
+ The values in this rule are alternatives. A product is included when its `AssociatedGroups` attribute equals
689
+ `0229/0x Platform63` OR `0229/1x Platform63`. Place the rule in the root `And` inclusion group alongside other
690
+ inclusion rules.
691
+
692
+ #### Client Migration
693
+
694
+ 1. Stop sending `appSettings.appSpecific.associatedGroups` to v2 endpoints.
695
+ 2. Add one rule with `code: "AssociatedGroups"` and `operator: "In"` to `appSettings.filters.inclusionCriteria.rules`.
696
+ 3. Copy every non-empty v1 list item to a separate `values[].V` entry.
697
+ 4. Preserve existing inclusion rules in the same root `And` group.
698
+
699
+ ### FrontEndFilter
700
+
701
+ #### v1
702
+
703
+ `AppSettings.AppSpecific.FrontEndFilter` excludes products whose `FrontEndFilter` attribute matches one of the
704
+ specified values:
705
+
706
+ ```json
707
+ {
708
+ "appSettings": {
709
+ "appSpecific": {
710
+ "frontEndFilter": ["FlameScanner", "LsoCATBXEcatalog"]
711
+ }
712
+ }
713
+ }
714
+ ```
715
+
716
+ #### v2
717
+
718
+ Move the non-empty values to one `FrontEndFilter` rule in `appSettings.filters.exclusionCriteria`:
719
+
720
+ ```json
721
+ {
722
+ "appSettings": {
723
+ "filters": {
724
+ "exclusionCriteria": {
725
+ "condition": "And",
726
+ "rules": [
727
+ {
728
+ "type": "attribute",
729
+ "code": "FrontEndFilter",
730
+ "values": [{ "V": "FlameScanner" }, { "V": "LsoCATBXEcatalog" }],
731
+ "operator": "In"
732
+ }
733
+ ]
734
+ }
735
+ }
736
+ }
737
+ }
738
+ ```
739
+
740
+ The values in this rule are alternatives. A product is excluded when its `FrontEndFilter` attribute equals
741
+ `FlameScanner` OR `LsoCATBXEcatalog`. Place the rule in the root `And` exclusion group alongside other exclusion
742
+ rules.
743
+
744
+ #### Client Migration
745
+
746
+ 1. Stop sending `appSettings.appSpecific.frontEndFilter` to v2 endpoints.
747
+ 2. Add one rule with `code: "FrontEndFilter"` and `operator: "In"` to `appSettings.filters.exclusionCriteria.rules`.
748
+ 3. Copy every non-empty v1 list item to a separate `values[].V` entry.
749
+ 4. Preserve existing exclusion rules in the same root `And` group.
750
+
751
+ ### Compatibility and Validation
752
+
753
+ - v1 requests and behavior are unchanged. During conversion, `associatedGroups` becomes an `AssociatedGroups` rule in the root `And` group of `inclusionCriteria`, and `frontEndFilter` becomes a `FrontEndFilter` rule in the root `And` group of `exclusionCriteria`.
754
+ - Each converted list becomes the `V` values of an `In` rule. Empty entries are discarded, matching the existing v1 query behavior.
755
+ - `AppSettingsInternal.AppSpecific` uses `AppSpecificSettings`, so the removed fields do not exist in the canonical execution model.
756
+ - Supported v1 and v2 app-specific settings are copied into that v2 internal contract.
757
+ - A v2 payload containing legacy `associatedGroups` or `frontEndFilter` is currently accepted and those unknown properties are ignored.
758
+ - The removed properties are absent from the v2 OpenAPI schema. Responses are unchanged.
759
+
760
+ ## Search TreeDepth
761
+
762
+ ### v1
763
+
764
+ The Search endpoint accepts `TreeDepth` as an application-specific setting. Although the property is part of the
765
+ shared `AppSettings.AppSpecific` contract, it is used only by `POST /v1/Products/Search`; other v1 controllers do not
766
+ consume it:
767
+
768
+ ```json
769
+ {
770
+ "dataTypes": ["Classifications"],
771
+ "appSettings": {
772
+ "appSpecific": {
773
+ "treeDepth": 2
774
+ }
775
+ }
776
+ }
777
+ ```
778
+
779
+ It controls how deeply the classification tree in the Search response is expanded. A missing value or `0` keeps the
780
+ default behavior.
781
+
782
+ ### v2
783
+
784
+ Move the property to the top level of `SearchQuery`:
785
+
786
+ ```json
787
+ {
788
+ "dataTypes": ["Classifications"],
789
+ "treeDepth": 2,
790
+ "appSettings": {
791
+ "appCode": "example"
792
+ }
793
+ }
794
+ ```
795
+
796
+ `TreeDepth` is an option of `POST /v2/Products/Search`, alongside other response-shaping settings such as
797
+ `MaxClassificationImages`. It is not part of the shared `SearchSettings` contract and does not affect product
798
+ matching. The property is nullable and defaults to `null`. A missing value, `null`, or `0` preserves the existing
799
+ default behavior.
800
+
801
+ ### Client Migration
802
+
803
+ 1. Stop sending `appSettings.appSpecific.treeDepth` to the v2 Search endpoint.
804
+ 2. Send the same integer as top-level `treeDepth` in `SearchQuery`.
805
+ 3. Keep `search.cid` and the remaining search criteria under `search` unchanged.
806
+
807
+ ### Compatibility and Validation
808
+
809
+ - The v1 request contract and behavior are unchanged. `SearchService` reads the legacy value from
810
+ `AppSettings.AppSpecific.TreeDepth` for v1 requests.
811
+ - The v2 Search endpoint reads only top-level `SearchQuery.TreeDepth` and treats `null` as `0`.
812
+ - A v2 payload containing legacy `appSettings.appSpecific.treeDepth` is currently accepted and the unknown property is
813
+ ignored.
814
+ - This move does not add range validation; accepted values retain the existing Search behavior.
815
+ - The classification response shape, facet selection behavior, and cache behavior are unchanged.
816
+ - `SearchSettings` remains unchanged because it is shared by other queries.
817
+ - The Classifications endpoint remains unchanged and continues to use `ClassificationSearchSettings.TreeDepth`.
818
+ - `TreeDepth` is removed from the v2 `AppSpecificSettings` OpenAPI schema and exposed as a nullable property directly
819
+ on `SearchQuery`.
820
+
821
+ ## AppSpecific.TableRowMatching
822
+
823
+ ### v1
824
+
825
+ `TableRowMatching` is an application-specific setting:
826
+
827
+ ```json
828
+ {
829
+ "appSettings": {
830
+ "appSpecific": {
831
+ "tableRowMatching": true
832
+ }
833
+ }
834
+ }
835
+ ```
836
+
837
+ When enabled, criteria for attributes stored in product tables are evaluated against individual rows instead of the
838
+ entire table data set.
839
+
840
+ ### v2
841
+
842
+ Move the property under `appSettings.filters` using the same name:
843
+
844
+ ```json
845
+ {
846
+ "appSettings": {
847
+ "filters": {
848
+ "tableRowMatching": true
849
+ }
850
+ }
851
+ }
852
+ ```
853
+
854
+ ### Client Migration
855
+
856
+ 1. Stop sending `appSettings.appSpecific.tableRowMatching` to v2 endpoints.
857
+ 2. Send the same boolean value as `appSettings.filters.tableRowMatching`.
858
+ 3. Keep table attribute criteria in the applicable inclusion, exclusion, or search filter settings.
859
+
860
+ ### Compatibility and Validation
861
+
862
+ - v1 requests and behavior are unchanged. The v1-to-internal-v2 conversion copies a true value from `AppSpecific` to `ApplicationFilters`.
863
+ - `AppSpecificSettings` no longer exposes `TableRowMatching`; `ApplicationFilters` exposes it with a default value of `false`.
864
+ - A v2 payload containing legacy `appSpecific.tableRowMatching` is currently accepted and the unknown property is ignored.
865
+ - The move changes only the v2 request shape. Table-row query and facet behavior are unchanged.
866
+
867
+ ## Detail RelatedProducts
868
+
869
+ ### v1
870
+
871
+ The Detail endpoint accepts `RelatedProducts` as an application-specific setting:
872
+
873
+ ```json
874
+ {
875
+ "dataTypes": ["ProductDetails"],
876
+ "appSettings": {
877
+ "appSpecific": {
878
+ "relatedProducts": true
879
+ }
880
+ }
881
+ }
882
+ ```
883
+
884
+ ### v2
885
+
886
+ Request related products directly as a Detail response section:
887
+
888
+ ```json
889
+ {
890
+ "dataTypes": ["RelatedProducts"],
891
+ "appSettings": {
892
+ "appCode": "example"
893
+ }
894
+ }
895
+ ```
896
+
897
+ `ProductDetails` and `RelatedProducts` are independent response sections. Include both values in `dataTypes` when
898
+ both sections are required.
899
+
900
+ ### Client Migration
901
+
902
+ 1. Stop sending `appSettings.appSpecific.relatedProducts` to the v2 Detail endpoint.
903
+ 2. Add `RelatedProducts` to the top-level `dataTypes` set when the related-products section is required.
904
+ 3. Add `ProductDetails` separately when the core product-detail section is also required.
905
+
906
+ ### Compatibility and Validation
907
+
908
+ - The v1 request contract is unchanged. For `DetailQuery`, a true legacy setting is converted to an effective
909
+ `RelatedProducts` data type without modifying the request object.
910
+ - `AppSpecificSettings` and the canonical internal app-specific settings no longer expose `RelatedProducts`.
911
+ - A v2 payload containing legacy `appSpecific.relatedProducts` is currently accepted and the unknown property is
912
+ ignored; only `dataTypes` controls the v2 response section.
913
+ - The response DTOs are unchanged. The service no longer returns `ProductDetails` implicitly when only related
914
+ products are requested, or related products when only product details are requested.
915
+ - The v2 OpenAPI schema exposes `RelatedProducts` in `DetailDataTypeEnum` and omits it from
916
+ `AppSpecificSettings`.
917
+
918
+ ## Product Variant Table Groups
919
+
920
+ V2 product details now support incremental loading of variant table groups. This reduces the initial Detail response
921
+ while preserving the complete v1 response contract.
922
+
923
+ ### v1
924
+
925
+ The v1 Detail endpoint continues to return `ProductVariantsTable` with every group containing both
926
+ `variantAttributes` and `products` when `ProductVariantsTable` is requested. The v1 request and response contracts are
927
+ unchanged.
928
+
929
+ ### v2 Detail
930
+
931
+ When `dataTypes` contains `ProductVariantsTable`, the first available group is returned with its complete
932
+ `variantAttributes` and `products` lists. Remaining groups contain only `attributeCode`, `attributeName`, and
933
+ `attributeValueText`; their `variantAttributes` and `products` properties are omitted from JSON.
934
+
935
+ ```json
936
+ {
937
+ "productVariantsTable": {
938
+ "items": [
939
+ {
940
+ "attributeCode": "Color",
941
+ "attributeName": "Color",
942
+ "attributeValueText": "Red",
943
+ "variantAttributes": [],
944
+ "products": []
945
+ },
946
+ {
947
+ "attributeCode": "Voltage",
948
+ "attributeName": "Voltage",
949
+ "attributeValueText": "230 V"
950
+ }
951
+ ]
952
+ }
953
+ }
954
+ ```
955
+
956
+ The first group means the first group with a non-empty value for the selected product, following the configured
957
+ variant attribute order. The response uses a v2-specific DTO with nullable sparse properties; v1 continues to use its
958
+ required properties.
959
+
960
+ ### Load One Group
961
+
962
+ Use the v2 endpoint below to load a complete group after receiving the sparse Detail response:
963
+
964
+ `POST /v2/Products/Variants/Table`
965
+
966
+ ```json
967
+ {
968
+ "search": {
969
+ "productId": "example-product"
970
+ },
971
+ "attributeCode": "Voltage",
972
+ "attributeCodes": ["CatalogNumber"],
973
+ "appSettings": {
974
+ "appCode": "example"
975
+ }
976
+ }
977
+ ```
978
+
979
+ The response is a `ProductVariantsTableSearchResultV2` wrapper containing exactly one full item:
980
+
981
+ ```json
982
+ {
983
+ "items": [
984
+ {
985
+ "attributeCode": "Voltage",
986
+ "attributeName": "Voltage",
987
+ "attributeValueText": "230 V",
988
+ "variantAttributes": [],
989
+ "products": []
990
+ }
991
+ ]
992
+ }
993
+ ```
994
+
995
+ `attributeCode` is required and must identify a visible variant group available for the requested product and
996
+ application context. Empty, unknown, unavailable, or inaccessible codes return `400 Bad Request`. The endpoint uses
997
+ the same application filters, country and tenant context, internal-data visibility, authorization, and short-lived
998
+ output caching as the Detail variant query. `attributeCodes` controls additional serialized attributes on returned
999
+ variant products.
1000
+
1001
+ ### Client Migration
1002
+
1003
+ 1. Request `ProductVariantsTable` from v2 Detail.
1004
+ 2. Render the first full group and retain metadata for the remaining groups.
1005
+ 3. Call `POST /v2/Products/Variants/Table` with the selected `attributeCode` when another group is needed.
1006
+ 4. Replace the sparse group with the returned full group.
1007
+
1008
+ An equivalent v2 Dropdown action is not part of this change and may be added separately in the `Variants` controller.
1009
+
1010
+ ### Compatibility and Validation
1011
+
1012
+ - V1 Detail behavior and DTOs are unchanged.
1013
+ - V2 Detail changes only the `ProductVariantsTable` response shape when that data type is requested.
1014
+ - Selector and Dropdown response sections keep their existing behavior.
1015
+ - The new endpoint is v2-only and uses the standard v2 authorization, validation, error, logging, and cache pipeline.
1016
+ - The new endpoint does not add pagination; the returned group may contain all matching variant products.
1017
+
1018
+ ## Filters and Filter Values
1019
+
1020
+ V2 separates filter discovery from loading selectable values. This reduces the response size of product Search and
1021
+ allows clients to request values only for filters that are currently expanded or otherwise needed.
1022
+
1023
+ - `POST /v2/Products/Filters` returns available filter metadata without `values`.
1024
+ - `POST /v2/Products/FilterValues` returns values for the selected `search.attributeCodes`.
1025
+ - `POST /v2/Products/RelationshipFilters` returns available relationship filter metadata without `values`.
1026
+ - `POST /v2/Products/RelationshipFilterValues` returns values for selected relationship filters.
1027
+ - `POST /v2/Products/Search` returns metadata without `values` in `filters` when `dataTypes` contains `Filters`.
1028
+ - `POST /v2/Catalogs/Products/Filters` returns available catalog filter metadata without `values`.
1029
+ - `POST /v2/Catalogs/Products/FilterValues` returns criterion-ready values for selected catalog filters.
1030
+
1031
+ ### v1
1032
+
1033
+ The v1 `Products/Filters` and `Products/RelationshipFilters` endpoints continue to accept attribute codes under
1034
+ `search.attributeCodes` and return `FilterValueSearchResult`. Each filter contains its selectable `values`, exposes
1035
+ both deprecated `isPromoted` and replacement `promoted`, and uses `name` plus an optional `code` for each value:
1036
+
1037
+ ```json
1038
+ {
1039
+ "items": [
1040
+ {
1041
+ "attributeCode": "Color",
1042
+ "attributeName": "Color",
1043
+ "attributeNameHtml": "Color",
1044
+ "hitCount": 42,
1045
+ "isPromoted": true,
1046
+ "promoted": true,
1047
+ "internal": false,
1048
+ "values": [
1049
+ {
1050
+ "name": "Red",
1051
+ "code": "RD",
1052
+ "hitCount": 17
1053
+ }
1054
+ ]
1055
+ }
1056
+ ]
1057
+ }
1058
+ ```
1059
+
1060
+ For ordinary text attributes, `code` can be absent or null and `name` contains the value to filter by. For
1061
+ enumerations, `name` is localized display text and `code` is the criterion value. For multidimensional attributes,
1062
+ `name` is formatted display text and `code` contains the encoded source value.
1063
+
1064
+ ### v2 Filter Discovery
1065
+
1066
+ Use `Products/Filters` or `Catalogs/Products/Filters` to retrieve product filter metadata:
1067
+
1068
+ ```json
1069
+ {
1070
+ "search": {
1071
+ "cid": "Root"
1072
+ },
1073
+ "filters": {
1074
+ "attributeCodes": ["Color", "Voltage"]
1075
+ },
1076
+ "appSettings": {
1077
+ "appCode": "example"
1078
+ }
1079
+ }
1080
+ ```
1081
+
1082
+ `filters.attributeCodes` is optional. When supplied, it selects the metadata to return and overrides the
1083
+ default AGM parametric or predefined filter list. The response contains no selectable values:
1084
+
1085
+ ```json
1086
+ {
1087
+ "items": [
1088
+ {
1089
+ "attributeCode": "Color",
1090
+ "attributeName": "Color",
1091
+ "attributeNameHtml": "Color",
1092
+ "hitCount": 42,
1093
+ "promoted": true,
1094
+ "internal": false
1095
+ }
1096
+ ],
1097
+ "totalCount": 12
1098
+ }
1099
+ ```
1100
+
1101
+ `totalCount` is the number of available filters before optional response limiting. The same metadata-only shape is
1102
+ used by `SearchResult.filters` and the v2 `RelationshipFilters` response. V2 omits obsolete `isPromoted`.
1103
+
1104
+ For `POST /v2/Products/Search`, optional top-level `filterSettings` is effective only when `dataTypes` contains
1105
+ `Filters`:
1106
+
1107
+ ```json
1108
+ {
1109
+ "dataTypes": ["Products", "Filters"],
1110
+ "filterSettings": {
1111
+ "maxFilterAttributes": 10,
1112
+ "attributeCodes": ["Color", "Voltage"]
1113
+ },
1114
+ "appSettings": {
1115
+ "appCode": "example"
1116
+ }
1117
+ }
1118
+ ```
1119
+
1120
+ V2 Search requires `dataTypes` to contain at least one value. Omitting it, sending `null`, or sending an empty array
1121
+ returns `400`. Clients should request only the response sections they consume; v1 Search validation remains
1122
+ unchanged.
1123
+
1124
+ `maxFilterAttributes` limits non-promoted filters. Promoted filters are not limited unless explicit
1125
+ `attributeCodes` are supplied.
1126
+
1127
+ ### v2 Filter Values
1128
+
1129
+ Use `Products/FilterValues` or `Catalogs/Products/FilterValues` to retrieve values for selected product filters.
1130
+ For both endpoints, `search.attributeCodes` is required,
1131
+ must be non-empty, accepts at most 100 codes, and every code must be non-empty:
1132
+
1133
+ ```json
1134
+ {
1135
+ "search": {
1136
+ "cid": "Root",
1137
+ "attributeCodes": ["Color"]
1138
+ },
1139
+ "appSettings": {
1140
+ "appCode": "example"
1141
+ }
1142
+ }
1143
+ ```
1144
+
1145
+ The response uses `FilterValueSearchResultV2`. Each selectable value replaces v1 `name` and `code` with `text` and a
1146
+ required criterion-ready `CriterionValue` object:
1147
+
1148
+ ```json
1149
+ {
1150
+ "items": [
1151
+ {
1152
+ "attributeCode": "Color",
1153
+ "attributeName": "Color",
1154
+ "attributeNameHtml": "Color",
1155
+ "hitCount": 42,
1156
+ "promoted": true,
1157
+ "internal": false,
1158
+ "values": [
1159
+ {
1160
+ "text": "Red",
1161
+ "value": { "V": "RD" },
1162
+ "hitCount": 17
1163
+ }
1164
+ ]
1165
+ }
1166
+ ]
1167
+ }
1168
+ ```
1169
+
1170
+ For ordinary text and table attributes, `text` is the display text and `value.V` contains the same raw facet value.
1171
+ For enumerations, `text` is the localized label and `value.V` contains the enumeration code. Multidimensional values
1172
+ are returned without their legacy encoded representation:
1173
+
1174
+ ```json
1175
+ {
1176
+ "text": "50 mm",
1177
+ "value": {
1178
+ "H": "",
1179
+ "V": "50",
1180
+ "U": "MM"
1181
+ },
1182
+ "hitCount": 8
1183
+ }
1184
+ ```
1185
+
1186
+ Relationship filters use the same split. `RelationshipFilters` accepts `PrmSearchSettings` plus optional
1187
+ `filters.attributeCodes`; `RelationshipFilterValues` accepts the relationship search fields and requires
1188
+ `search.attributeCodes`. Both preserve the PRM block-join facet domain and relationship-specific AGM ordering.
1189
+
1190
+ ### Client Migration
1191
+
1192
+ 1. Use `Filters` or `Search` with `dataTypes: ["Filters"]` to discover available filter metadata.
1193
+ 2. When the client needs selectable product or catalog values, call the corresponding `FilterValues` endpoint with those codes in `search.attributeCodes`.
1194
+ 3. Apply the same flow to `RelationshipFilters` and `RelationshipFilterValues` for PRM results.
1195
+ 4. Read display text from `values[].text` and pass `values[].value` directly to a v2 attribute criterion.
1196
+ 5. Preserve all returned `H`, `V`, and `U` fields for multidimensional filters; do not parse display text.
1197
+ 6. Read only `promoted`; obsolete `isPromoted` is not present in v2 responses.
1198
+
1199
+ ### Compatibility and Validation
1200
+
1201
+ - V1 `Filters` and `RelationshipFilters` routes, requests, and `FilterValueSearchResult` responses are unchanged.
1202
+ - Sending a former v2 product or catalog `Filters` request with `search.attributeCodes` no longer requests values. The
1203
+ unknown `attributeCodes` property under `search` is ignored by the current JSON configuration; use `filters`
1204
+ for metadata selection or the corresponding `FilterValues` endpoint for values.
1205
+ - V2 `FilterValues`, `RelationshipFilterValues`, and catalog `FilterValues` reject missing, null, empty, over-100, or
1206
+ blank attribute-code selections.
1207
+ - Existing product search criteria and recursive `search.filter` criteria remain available on the product, relationship,
1208
+ and catalog v2 filter endpoints.
1209
+ - Product matching, relationship matching, hit counts, localization, criterion-ready values, and HTTP status behavior
1210
+ are unchanged.
1211
+ - `POST /v2/Catalogs/Products/Filters` returns metadata and `POST /v2/Catalogs/Products/FilterValues` returns values.
1212
+ - V2 OpenAPI exposes `FilterSearchResult` for discovery and `FilterValueSearchResult`, `FilterValueItem`, and
1213
+ `FilterValue` for value responses after the global v2 schema-name rule removes implementation suffixes.
1214
+ - Relationship v1 service tests cover the compatibility path; focused v2 response-contract tests remain pending.
1215
+
1216
+ ## Interactive Guides
1217
+
1218
+ The v2 OpenAPI document omits `InteractiveGuides` from `SearchDataTypeEnum` and `DetailDataTypeEnum`. It also omits
1219
+ the obsolete `interactiveGuides` response properties from Search and Detail responses. The v1 OpenAPI document
1220
+ continues to expose these enum values and response properties for backward compatibility.
1221
+
1222
+ Clients migrating to `POST /v2/Products/Search` or `POST /v2/Products/Detail` must stop including
1223
+ `"InteractiveGuides"` in `dataTypes`. Interactive guides are no longer populated in responses.
1224
+
1225
+ This is currently an OpenAPI contract change only. Because v1 and v2 share the underlying .NET enum types, the v2
1226
+ JSON converter can still deserialize `"InteractiveGuides"`; v2 validation does not reject it. Runtime rejection
1227
+ would require a separate validation change or dedicated v2 enum types.
1228
+
1229
+ Verification confirms that the served v1 OpenAPI document retains `InteractiveGuides`, while the served v2 OpenAPI
1230
+ document contains no `InteractiveGuides` enum values, descriptions, or response properties.
1231
+
1232
+ ## OpenAPI Schema Names
1233
+
1234
+ The v2 OpenAPI document removes the trailing `V2` suffix from component schema names. This keeps implementation
1235
+ versioning details out of the public model names displayed by Scalar and consumed by OpenAPI client generators.
1236
+
1237
+ The rule has no collision exceptions. Version-specific Compare and Favorite Views responses ensure that legacy
1238
+ `AttributeGroup` and `SearchSettings` schemas are not referenced by v2 operations. Their v2 counterparts are exposed
1239
+ as `AttributeGroup` and `SearchSettings` in the v2 document.
1240
+
1241
+ Polymorphic types derived from `Criterion` also use their direct type names. The v2 component schemas are exposed as
1242
+ `AttributeCriterion` and `CriteriaGroup`, rather than the default concatenated names `CriterionAttributeCriterion`
1243
+ and `CriterionCriteriaGroup`. The base schema remains `Criterion`, and its discriminator mappings and `$ref` values
1244
+ point to the shortened component names.
1245
+
1246
+ This change affects only schema keys under `components.schemas` and their `$ref` values in the v2 OpenAPI document.
1247
+ It does not rename C# types, JSON properties, routes, request or response payloads, or schemas in the v1 OpenAPI
1248
+ document. Clients regenerated from the v2 specification may receive model type renames even though their serialized
1249
+ JSON contracts are unchanged.
1250
+
1251
+ ## Translation Domains
1252
+
1253
+ `POST /v2/Translations` replaces free-form component names with a fixed set of translation domains. The response
1254
+ uses the same domain enum and no longer exposes the legacy component property.
1255
+
1256
+ ### v1
1257
+
1258
+ ```json
1259
+ {
1260
+ "components": ["products", "common"],
1261
+ "appSettings": {
1262
+ "appCode": "example"
1263
+ }
1264
+ }
1265
+ ```
1266
+
1267
+ The v1 response continues to use a string `component`:
1268
+
1269
+ ```json
1270
+ [
1271
+ {
1272
+ "component": "products",
1273
+ "key": "Product.Title",
1274
+ "value": "Product"
1275
+ }
1276
+ ]
1277
+ ```
1278
+
1279
+ ### v2
1280
+
1281
+ ```json
1282
+ {
1283
+ "domains": ["Products", "Common"],
1284
+ "appSettings": {
1285
+ "appCode": "example"
1286
+ }
1287
+ }
1288
+ ```
1289
+
1290
+ `domains` accepts only `Catalogs`, `Classifications`, `Common`, and `Products`. If it is omitted, null, or empty,
1291
+ translations from all four supported domains are returned.
1292
+
1293
+ The v2 response uses `TranslationItemV2`, where `domain` is the same enum:
1294
+
1295
+ ```json
1296
+ [
1297
+ {
1298
+ "domain": "Products",
1299
+ "key": "Product.Title",
1300
+ "value": "Product"
1301
+ }
1302
+ ]
1303
+ ```
1304
+
1305
+ ### Client Migration
1306
+
1307
+ 1. Replace `components` with `domains` in v2 translation requests.
1308
+ 2. Change lowercase free-form component strings to the corresponding enum names.
1309
+ 3. Read `domain` instead of `component` from each v2 response item.
1310
+
1311
+ ### Compatibility and Validation
1312
+
1313
+ - The v1 request and response contracts are unchanged.
1314
+ - A v2 payload containing legacy `components` is accepted by the current JSON configuration, but the unknown
1315
+ property is ignored; omitting `domains` therefore returns all supported domains.
1316
+ - Values outside the four declared domains are rejected during v2 request binding.
1317
+ - Translation lookup, language fallback, output caching, and HTTP status behavior are unchanged.
1318
+ - The v2 OpenAPI operation exposes enum-based `domains` in the request and enum-based `domain` in its response.
1319
+
1320
+ ## Query-Scoped Relationship and Variant Settings
1321
+
1322
+ V2 no longer exposes `productRelationshipFiltering` or `variant` inside `appSettings`. These options describe the
1323
+ result of the current query, so they are now scoped to the query that uses them. The v1 `AppSettings` contract and all
1324
+ v1 behavior remain unchanged.
1325
+
1326
+ `variants` uses `VariantSettingsV2`:
1327
+
1328
+ ```json
1329
+ {
1330
+ "variants": {
1331
+ "clustering": true,
1332
+ "attributeCodes": ["Color", "Voltage_*"]
1333
+ }
1334
+ }
1335
+ ```
1336
+
1337
+ `clustering` controls the Solr variant collapse for product search results, counts, filter metadata, filter values,
1338
+ and autocomplete. `attributeCodes` requests additional serialized product attributes. In `Detail`, it applies to the
1339
+ variant products returned by the selected variant data types.
1340
+
1341
+ `relationships` uses `RelationshipSettingsV2`:
1342
+
1343
+ ```json
1344
+ {
1345
+ "relationships": {
1346
+ "productRelationshipFiltering": true,
1347
+ "attributeCodes": ["RelationshipAttribute", "Voltage_*"],
1348
+ "pager": {
1349
+ "page": 1,
1350
+ "pageSize": 20
1351
+ }
1352
+ }
1353
+ }
1354
+ ```
1355
+
1356
+ The relationship settings control relationship filter metadata, additional attributes for related products, and public
1357
+ pagination of relationship products. In `Detail`, the existing internal `relationshipPager` is replaced by the public
1358
+ `relationships.pager`. `PrmQueryV2` and `PrmAutocompleteQueryV2` likewise use `relationships.pager`; their former
1359
+ top-level v2 pager is no longer used. `PrmQueryV2` also places relationship attribute selection under
1360
+ `relationships.attributeCodes`.
1361
+
1362
+ The query coverage is:
1363
+
1364
+ | Setting | v2 query contracts |
1365
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
1366
+ | `variants` | `SearchQueryV2`, `AutocompleteQueryV2`, `CountQueryV2`, `FiltersQueryV2`, `FilterValueQueryV2`, `DetailQueryV2`, and `ClassificationQueryV2` |
1367
+ | `relationships` | `DetailQueryV2`, `PrmQueryV2`, `PrmFiltersQueryV2`, `PrmFilterValueQueryV2`, and `PrmAutocompleteQueryV2` |
1368
+
1369
+ Legacy v2 payloads that still put these properties under `appSettings` are not transformed; the old properties are
1370
+ unknown to `AppSettingsV2` and are ignored by JSON binding. Clients must move them to the query-level sections above.
1371
+
1372
+ ## Query-Scoped Attribute Sorting
1373
+
1374
+ V2 attribute sort mappings are no longer supplied through `appSettings.sort`. The v2 `SearchQuery` and
1375
+ `PriceListQuery` contracts expose `SortSettingsV2`, which combines the regular field/direction sort with optional
1376
+ attribute mappings:
1377
+
1378
+ ```json
1379
+ {
1380
+ "sort": {
1381
+ "field": "Relevance",
1382
+ "direction": "Descending",
1383
+ "map": [
1384
+ {
1385
+ "attributeCode": "ProductStatus",
1386
+ "values": ["Preferred", "Standard"]
1387
+ }
1388
+ ]
1389
+ },
1390
+ "appSettings": {
1391
+ "appCode": "example"
1392
+ }
1393
+ }
1394
+ ```
1395
+
1396
+ `sort.map` is supported by `SearchQueryV2` and `PriceListQueryV2` only. The map is applied to the Solr request for
1397
+ the current query. `Count`, `Filters`, `FilterValues`, `Detail`, autocomplete, and relationship queries do not accept
1398
+ this setting. The v1 `AppSettings.Sort` contract remains unchanged.
1399
+
1400
+ ## Endpoint Rollout
1401
+
1402
+ | Endpoint | v2 request contract | Filter execution | Status |
1403
+ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------- |
1404
+ | `POST /v2/Translations` | `TranslationQuery` with enum-based `Domains` and `TranslationItem` response with enum-based `Domain` | Not applicable | Implemented |
1405
+ | `POST /v2/Sitemap/Products` | `SitemapProductSearchQuery` with `AppSettings`; `SitemapProductSearchSettings` is unchanged | Flat scalar `Criteria` is mapped to the v1 Solr filter path; nested and multidimensional criteria return `400` | Implemented |
1406
+ | `POST /v2/Products/Search` | `SearchQuery` with optional `filterSettings` when `dataTypes` contains `Filters` | Returns filter metadata only; requested codes and maximum count can reduce the filter response | Implemented |
1407
+ | `POST /v2/Catalogs/Products/Search` | `CatalogProductSearchQueryV2` with optional `filterSettings` when `dataTypes` contains `Filters` | Returns catalog filter metadata with the requested codes and maximum count applied | Implemented |
1408
+ | `POST /v2/Products/Filters` | `FiltersQuery` with optional search and metadata `filters` | Returns available `FilterSearchResult` metadata without selectable values | Implemented |
1409
+ | `POST /v2/Products/FilterValues` | `FilterValueQuery` with required `search.attributeCodes` | Returns criterion-ready `FilterValueSearchResult` values for selected attributes | Implemented |
1410
+ | `POST /v2/Products/RelationshipFilters` | `PrmFiltersQuery` with optional metadata `filters` | Returns relationship filter metadata using the PRM block-join domain | Implemented |
1411
+ | `POST /v2/Products/RelationshipFilterValues` | `PrmFilterValueQuery` with required `search.attributeCodes` | Returns criterion-ready relationship filter values using the PRM block-join domain | Implemented |
1412
+ | `POST /v2/Catalogs/Products/Filters` | `CatalogProductFiltersQueryV2` with optional metadata `filters` | Returns catalog product filter metadata | Implemented |
1413
+ | `POST /v2/Catalogs/Products/FilterValues` | `CatalogProductFilterValuesQueryV2` with required `search.attributeCodes` | Returns criterion-ready catalog filter values | Implemented |
1414
+ | Product search, count, list, autocomplete, and related endpoints | To be added | Pending `Criteria` query compilation | Not started |
1415
+
1416
+ ## Update Checklist
1417
+
1418
+ When adding the next v2 migration item:
1419
+
1420
+ 1. Add the v1 and v2 JSON examples.
1421
+ 2. State whether legacy input is ignored, rejected, or transformed.
1422
+ 3. Identify endpoint coverage and response impact.
1423
+ 4. Add validation, service, query-composition, cache, and OpenAPI status.
1424
+ 5. Add focused tests and update the change-log status.