@tillhub/schemas 6.456.0 → 6.457.0

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.
@@ -374,6 +374,49 @@ module.exports = oneOf({
374
374
  }
375
375
  })
376
376
  }
377
+ }),
378
+ press_item_barcode: oneOf({
379
+ description: 'German press products carry their retail price and VAT class inside the EAN-13 barcode, so they can be sold without being maintained in the catalog.',
380
+ type: 'object',
381
+ additionalProperties: false,
382
+ properties: {
383
+ enabled: {
384
+ description: 'Specifies if the feature is available. When false, behaviour is identical to a client without the feature.',
385
+ type: 'boolean',
386
+ default: false
387
+ },
388
+ lookup_priority: {
389
+ description: 'Which side is asked first for a scanned code, i.e. which article carries the line. \'catalog_first\' searches the catalog and decodes only what it does not find, \'barcode_first\' decodes before searching. An unrecognised value falls back to the default.',
390
+ type: 'string',
391
+ enum: [
392
+ 'catalog_first',
393
+ 'barcode_first'
394
+ ],
395
+ default: 'catalog_first'
396
+ },
397
+ price_source_on_catalog_hit: {
398
+ description: 'Which price is booked when a decodable press code also matches a catalog article. \'barcode\' charges the encoded amount and uses the article\'s own price only as the value it is compared against, \'catalog\' charges the article\'s own price and falls back to the encoded amount when it has none. A disagreement between the two is logged under either setting. The field is inert wherever no catalog hit occurs, which includes every scan under \'barcode_first\' - that combination is contradictory but not an error and must not be rejected. An unrecognised value falls back to the default.',
399
+ type: 'string',
400
+ enum: [
401
+ 'barcode',
402
+ 'catalog'
403
+ ],
404
+ default: 'barcode'
405
+ },
406
+ products: {
407
+ description: 'Maps a press prefix to the uuid of the fallback article that supplies VAT and accounting information for that prefix. The supported prefixes are the keys, not a fixed set, so adding one needs no client release. Enabled with an empty map is equivalent to disabled.',
408
+ type: 'object',
409
+ additionalProperties: false,
410
+ patternProperties: {
411
+ '^[0-9]{3}$': {
412
+ description: 'Uuid of the fallback article for this press prefix.',
413
+ type: 'string',
414
+ format: 'uuid'
415
+ }
416
+ },
417
+ default: {}
418
+ }
419
+ }
377
420
  })
378
421
  }
379
422
  })
@@ -1,262 +1,257 @@
1
1
  const { oneOf } = require('../../../helpers/payload-or-null')
2
2
 
3
3
  module.exports = {
4
- anyOf: [
5
- {
6
- type: 'null'
7
- },
8
- {
4
+ type: ['object', 'null'],
5
+ additionalProperties: false,
6
+ properties: {
7
+ scanner_apis: {
9
8
  type: 'object',
10
- additionalProperties: false,
9
+ default: {},
11
10
  properties: {
12
- scanner_apis: {
11
+ zebra: {
13
12
  type: 'object',
13
+ default: {},
14
14
  properties: {
15
- zebra: {
16
- type: 'object',
17
- properties: {
18
- enabled: {
19
- type: 'boolean',
20
- default: false
21
- },
22
- background_release_seconds: {
23
- description: 'The time in seconds that the scanner will stay connected when the app is send to the background (for other apps to grab afterwards)',
24
- type: 'integer',
25
- example: 15,
26
- minimum: 0,
27
- maximum: 30, // to be on the safe side of Apple
28
- default: 25
29
- }
30
- }
15
+ enabled: {
16
+ type: 'boolean',
17
+ default: true
31
18
  },
32
- epson: {
33
- type: 'object',
34
- properties: {
35
- enabled: {
36
- type: 'boolean',
37
- default: true
38
- },
39
- background_release_seconds: {
40
- description: 'The time in seconds that the scanner will stay connected when the app is send to the background (for other apps to grab afterwards)',
41
- type: 'integer',
42
- example: 15,
43
- minimum: 0,
44
- maximum: 30, // to be on the safe side of Apple
45
- default: 25
46
- }
47
- }
19
+ background_release_seconds: {
20
+ description: 'The time in seconds that the scanner will stay connected when the app is send to the background (for other apps to grab afterwards)',
21
+ type: 'integer',
22
+ example: 15,
23
+ minimum: 0,
24
+ maximum: 30, // to be on the safe side of Apple
25
+ default: 25
48
26
  }
49
27
  }
50
28
  },
51
- printer_apis: {
29
+ epson: {
52
30
  type: 'object',
53
31
  properties: {
54
- epson: {
55
- type: 'object',
56
- properties: {
57
- enabled: {
58
- type: 'boolean',
59
- default: true
60
- },
61
- pre_connect: oneOf({
62
- description: 'Try connecting to the printer without an actual printing task',
63
- type: 'array',
64
- items: {
65
- description: 'Views or conditions where a printer connection is tried just in case',
66
- type: 'string',
67
- enum: [
68
- 'payment_view'
69
- ]
70
- }
71
- }),
72
- allow_force_narrow: oneOf({
73
- description: 'Potentially allow forcing 60mm-print on 80mm-printers via UI toggle ',
74
- type: 'boolean',
75
- default: false
76
- })
77
- }
32
+ enabled: {
33
+ type: 'boolean',
34
+ default: true
78
35
  },
79
- epson_fp_it: {
80
- type: 'object',
81
- properties: {
82
- enabled: {
83
- type: 'boolean',
84
- default: false
85
- }
36
+ background_release_seconds: {
37
+ description: 'The time in seconds that the scanner will stay connected when the app is send to the background (for other apps to grab afterwards)',
38
+ type: 'integer',
39
+ example: 15,
40
+ minimum: 0,
41
+ maximum: 30, // to be on the safe side of Apple
42
+ default: 25
43
+ }
44
+ }
45
+ }
46
+ }
47
+ },
48
+ printer_apis: {
49
+ type: 'object',
50
+ properties: {
51
+ epson: {
52
+ type: 'object',
53
+ properties: {
54
+ enabled: {
55
+ type: 'boolean',
56
+ default: true
57
+ },
58
+ pre_connect: oneOf({
59
+ description: 'Try connecting to the printer without an actual printing task',
60
+ type: 'array',
61
+ items: {
62
+ description: 'Views or conditions where a printer connection is tried just in case',
63
+ type: 'string',
64
+ enum: [
65
+ 'payment_view'
66
+ ]
86
67
  }
68
+ }),
69
+ allow_force_narrow: oneOf({
70
+ description: 'Potentially allow forcing 60mm-print on 80mm-printers via UI toggle ',
71
+ type: 'boolean',
72
+ default: false
73
+ })
74
+ }
75
+ },
76
+ epson_fp_it: {
77
+ type: 'object',
78
+ properties: {
79
+ enabled: {
80
+ type: 'boolean',
81
+ default: false
87
82
  }
88
83
  }
84
+ }
85
+ }
86
+ },
87
+ terminal_apis: {
88
+ type: 'object',
89
+ properties: {
90
+ adyen: {
91
+ type: 'object',
92
+ properties: {
93
+ enabled: {
94
+ type: 'boolean',
95
+ default: true
96
+ },
97
+ webserver: oneOf({
98
+ description: 'Defines which webserver implementation to use for secondary cashier messages (terminal -> POS)',
99
+ type: 'string',
100
+ enum: [
101
+ 'none', // messages will not be visible on the POS but overall functionality is not affected
102
+ 'criollo', // there have been issues (BSH)
103
+ 'telegraph' // more stable than criollo
104
+ ],
105
+ default: 'telegraph'
106
+ }),
107
+ inter_call_delay_seconds: oneOf({
108
+ type: 'number',
109
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
110
+ minimum: 0.0,
111
+ maximum: 10.0,
112
+ multipleOf: 0.001,
113
+ default: 0.0
114
+ })
115
+ }
89
116
  },
90
- terminal_apis: {
117
+ concardis: {
91
118
  type: 'object',
92
119
  properties: {
93
- adyen: {
94
- type: 'object',
95
- properties: {
96
- enabled: {
97
- type: 'boolean',
98
- default: true
99
- },
100
- webserver: oneOf({
101
- description: 'Defines which webserver implementation to use for secondary cashier messages (terminal -> POS)',
102
- type: 'string',
103
- enum: [
104
- 'none', // messages will not be visible on the POS but overall functionality is not affected
105
- 'criollo', // there have been issues (BSH)
106
- 'telegraph' // more stable than criollo
107
- ],
108
- default: 'telegraph'
109
- }),
110
- inter_call_delay_seconds: oneOf({
111
- type: 'number',
112
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
113
- minimum: 0.0,
114
- maximum: 10.0,
115
- multipleOf: 0.001,
116
- default: 0.0
117
- })
118
- }
120
+ enabled: {
121
+ type: 'boolean',
122
+ default: true
119
123
  },
120
- concardis: {
121
- type: 'object',
122
- properties: {
123
- enabled: {
124
- type: 'boolean',
125
- default: true
126
- },
127
- inter_call_delay_seconds: oneOf({
128
- type: 'number',
129
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
130
- minimum: 0.0,
131
- maximum: 10.0,
132
- multipleOf: 0.001,
133
- default: 0.0
134
- })
135
- }
124
+ inter_call_delay_seconds: oneOf({
125
+ type: 'number',
126
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
127
+ minimum: 0.0,
128
+ maximum: 10.0,
129
+ multipleOf: 0.001,
130
+ default: 0.0
131
+ })
132
+ }
133
+ },
134
+ opi: {
135
+ type: 'object',
136
+ properties: {
137
+ enabled: {
138
+ type: 'boolean',
139
+ default: true
136
140
  },
137
- opi: {
138
- type: 'object',
139
- properties: {
140
- enabled: {
141
- type: 'boolean',
142
- default: true
143
- },
144
- swiss: oneOf({
145
- description: 'Defines if swiss-specific handling of the OPI protocol will be used',
146
- type: 'boolean',
147
- default: false
148
- }),
149
- omit_amount_on_reversals: oneOf({
150
- description: 'Defines if amount can be omitted in reversals',
151
- type: 'boolean',
152
- default: false
153
- }),
154
- inter_call_delay_seconds: oneOf({
155
- type: 'number',
156
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
157
- minimum: 0.0,
158
- maximum: 10.0,
159
- multipleOf: 0.001,
160
- default: 1.0 // defaults to 3.0 if Swiss
161
- }),
162
- language_code: oneOf({
163
- type: 'string',
164
- description: 'Language code send in card service requests. ISO 639-1:2002.',
165
- example: 'de',
166
- minLength: 2,
167
- maxLength: 2,
168
- default: null // fallback is defined by the current POS language
169
- })
170
- }
141
+ swiss: oneOf({
142
+ description: 'Defines if swiss-specific handling of the OPI protocol will be used',
143
+ type: 'boolean',
144
+ default: false
145
+ }),
146
+ omit_amount_on_reversals: oneOf({
147
+ description: 'Defines if amount can be omitted in reversals',
148
+ type: 'boolean',
149
+ default: false
150
+ }),
151
+ inter_call_delay_seconds: oneOf({
152
+ type: 'number',
153
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
154
+ minimum: 0.0,
155
+ maximum: 10.0,
156
+ multipleOf: 0.001,
157
+ default: 1.0 // defaults to 3.0 if Swiss
158
+ }),
159
+ language_code: oneOf({
160
+ type: 'string',
161
+ description: 'Language code send in card service requests. ISO 639-1:2002.',
162
+ example: 'de',
163
+ minLength: 2,
164
+ maxLength: 2,
165
+ default: null // fallback is defined by the current POS language
166
+ })
167
+ }
168
+ },
169
+ sumup: {
170
+ type: 'object',
171
+ properties: {
172
+ enabled: {
173
+ type: 'boolean',
174
+ default: true
171
175
  },
172
- sumup: {
173
- type: 'object',
174
- properties: {
175
- enabled: {
176
- type: 'boolean',
177
- default: true
178
- },
179
- inter_call_delay_seconds: oneOf({
180
- type: 'number',
181
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
182
- minimum: 0.0,
183
- maximum: 10.0,
184
- multipleOf: 0.001,
185
- default: 0.0
186
- })
187
- }
176
+ inter_call_delay_seconds: oneOf({
177
+ type: 'number',
178
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
179
+ minimum: 0.0,
180
+ maximum: 10.0,
181
+ multipleOf: 0.001,
182
+ default: 0.0
183
+ })
184
+ }
185
+ },
186
+ tim: {
187
+ type: 'object',
188
+ properties: {
189
+ enabled: {
190
+ type: 'boolean',
191
+ default: true
188
192
  },
189
- tim: {
190
- type: 'object',
191
- properties: {
192
- enabled: {
193
- type: 'boolean',
194
- default: true
195
- },
196
- inter_call_delay_seconds: oneOf({
197
- type: 'number',
198
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
199
- minimum: 0.0,
200
- maximum: 10.0,
201
- multipleOf: 0.001,
202
- default: 0.0
203
- })
204
- }
193
+ inter_call_delay_seconds: oneOf({
194
+ type: 'number',
195
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
196
+ minimum: 0.0,
197
+ maximum: 10.0,
198
+ multipleOf: 0.001,
199
+ default: 0.0
200
+ })
201
+ }
202
+ },
203
+ spos: {
204
+ type: 'object',
205
+ properties: {
206
+ enabled: {
207
+ type: 'boolean',
208
+ default: true
205
209
  },
206
- spos: {
207
- type: 'object',
208
- properties: {
209
- enabled: {
210
- type: 'boolean',
211
- default: true
212
- },
213
- inter_call_delay_seconds: oneOf({
214
- type: 'number',
215
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
216
- minimum: 0.0,
217
- maximum: 10.0,
218
- multipleOf: 0.001,
219
- default: 0.0
220
- })
221
- }
210
+ inter_call_delay_seconds: oneOf({
211
+ type: 'number',
212
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
213
+ minimum: 0.0,
214
+ maximum: 10.0,
215
+ multipleOf: 0.001,
216
+ default: 0.0
217
+ })
218
+ }
219
+ },
220
+ softpay: {
221
+ type: 'object',
222
+ properties: {
223
+ enabled: {
224
+ type: 'boolean',
225
+ default: true
222
226
  },
223
- softpay: {
224
- type: 'object',
225
- properties: {
226
- enabled: {
227
- type: 'boolean',
228
- default: true
229
- },
230
- inter_call_delay_seconds: oneOf({
231
- type: 'number',
232
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
233
- minimum: 0.0,
234
- maximum: 10.0,
235
- multipleOf: 0.001,
236
- default: 0.0
237
- })
238
- }
227
+ inter_call_delay_seconds: oneOf({
228
+ type: 'number',
229
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
230
+ minimum: 0.0,
231
+ maximum: 10.0,
232
+ multipleOf: 0.001,
233
+ default: 0.0
234
+ })
235
+ }
236
+ },
237
+ ZVT: { // capitalized for historical reasons
238
+ type: 'object',
239
+ properties: {
240
+ enabled: {
241
+ type: 'boolean',
242
+ default: true
239
243
  },
240
- ZVT: { // capitalized for historical reasons
241
- type: 'object',
242
- properties: {
243
- enabled: {
244
- type: 'boolean',
245
- default: true
246
- },
247
- inter_call_delay_seconds: oneOf({
248
- type: 'number',
249
- description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
250
- minimum: 0.0,
251
- maximum: 10.0,
252
- multipleOf: 0.001,
253
- default: 0.0
254
- })
255
- }
256
- }
244
+ inter_call_delay_seconds: oneOf({
245
+ type: 'number',
246
+ description: 'Artificial delay between a previous terminal response and the next terminal request when performed automatically (some devices require additional cooldowns to avoid \'busy\' states)',
247
+ minimum: 0.0,
248
+ maximum: 10.0,
249
+ multipleOf: 0.001,
250
+ default: 0.0
251
+ })
257
252
  }
258
253
  }
259
254
  }
260
255
  }
261
- ]
256
+ }
262
257
  }
@@ -148,6 +148,11 @@ const cartReceipt = {
148
148
  type: 'boolean',
149
149
  default: true
150
150
  }),
151
+ print_barcode: oneOf({
152
+ description: 'Defines if the scanned barcode of a press item is printed on the specific receipt, as its own line below the product name. It governs press lines booked against a titled article, whose own name already identifies them. A line booked against one of the generic press fallback articles configured in features.press_item_barcode.products prints its barcode regardless of this flag, because the barcode is the only thing that tells two such lines apart. Defaults to true like print_serial_number, and is inert until that prefix map is populated. Per-receipt-type defaults are applied by the client, since this object is shared by every cart receipt type.',
153
+ type: 'boolean',
154
+ default: true
155
+ }),
151
156
  print_service_question_answers: oneOf({
152
157
  description: 'States if the product customization answers and questions will be printed',
153
158
  type: 'boolean',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tillhub/schemas",
3
- "version": "6.456.0",
3
+ "version": "6.457.0",
4
4
  "private": false,
5
5
  "description": "All Tillhub API OpenAPI 3 compatible JSON Schemas.",
6
6
  "main": "./lib/index.js",
@@ -17,7 +17,8 @@
17
17
  "clean": "rm -rf coverage coverage.lcov .nyc_output",
18
18
  "deploy:static": "firebase deploy --only=hosting",
19
19
  "compile": "node compile",
20
- "new:schema": "hygen schema with-prompt"
20
+ "new:schema": "hygen schema with-prompt",
21
+ "commitlint": "commitlint --from origin/master --to HEAD --verbose"
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
@@ -51,6 +52,8 @@
51
52
  ],
52
53
  "homepage": "https://github.com/tillhub/schemas#readme",
53
54
  "devDependencies": {
55
+ "@commitlint/cli": "^19.8.1",
56
+ "@commitlint/config-conventional": "^19.8.1",
54
57
  "@semantic-release/changelog": "^6.0.3",
55
58
  "@semantic-release/exec": "^7.1.0",
56
59
  "@semantic-release/git": "^10.0.1",
@@ -185,3 +185,156 @@ test('Configurations: payloads without opening_hours_exceptions remain valid', f
185
185
  t.ok(valid, ajv.errorsText(validateUpdatePayload.errors))
186
186
  t.end()
187
187
  })
188
+
189
+ test('Configurations: interfaces seeds scanner_apis.zebra.enabled when scanner_apis is absent', function (t) {
190
+ t.plan(2)
191
+ const ajvWithDefaults = new Ajv({ useDefaults: true })
192
+ const validateCreatePayload = ajvWithDefaults.compile(require('../../lib/v1/configurations').create.request)
193
+ const payload = {
194
+ name: 'Default Configurations',
195
+ owner: 'self',
196
+ interfaces: {
197
+ terminal_apis: {
198
+ opi: { enabled: true },
199
+ tim: { enabled: true },
200
+ adyen: { enabled: true },
201
+ sumup: { enabled: true },
202
+ concardis: { enabled: true }
203
+ }
204
+ }
205
+ }
206
+ const valid = validateCreatePayload(payload)
207
+
208
+ t.ok(valid, ajvWithDefaults.errorsText(validateCreatePayload.errors))
209
+ t.deepEquals(payload.interfaces.scanner_apis, {
210
+ zebra: {
211
+ enabled: true,
212
+ background_release_seconds: 25
213
+ }
214
+ }, 'zebra is enabled by default')
215
+ t.end()
216
+ })
217
+
218
+ test('Configurations: interfaces keeps an explicitly disabled zebra scanner', function (t) {
219
+ t.plan(2)
220
+ const ajvWithDefaults = new Ajv({ useDefaults: true })
221
+ const validateCreatePayload = ajvWithDefaults.compile(require('../../lib/v1/configurations').create.request)
222
+ const payload = {
223
+ name: 'Default Configurations',
224
+ owner: 'self',
225
+ interfaces: {
226
+ scanner_apis: {
227
+ zebra: { enabled: false }
228
+ }
229
+ }
230
+ }
231
+ const valid = validateCreatePayload(payload)
232
+
233
+ t.ok(valid, ajvWithDefaults.errorsText(validateCreatePayload.errors))
234
+ t.notOk(payload.interfaces.scanner_apis.zebra.enabled, 'explicit value wins over the default')
235
+ t.end()
236
+ })
237
+
238
+ test('Configurations: interfaces still accepts null', function (t) {
239
+ t.plan(2)
240
+ const validCreate = ajv.compile(require('../../lib/v1/configurations').create.request)({
241
+ name: 'Default Configurations',
242
+ owner: 'self',
243
+ interfaces: null
244
+ })
245
+ t.ok(validCreate, 'null interfaces is valid on create')
246
+
247
+ const validUpdate = validateUpdatePayload({ interfaces: null })
248
+ t.ok(validUpdate, ajv.errorsText(validateUpdatePayload.errors))
249
+ t.end()
250
+ })
251
+
252
+ const pressUuid = '5b1b1b1b-1b1b-4b1b-8b1b-1b1b1b1b1b1b'
253
+
254
+ test('Configurations: press_item_barcode accepts the documented contract', function (t) {
255
+ t.plan(1)
256
+ const valid = validateUpdatePayload({
257
+ features: {
258
+ press_item_barcode: {
259
+ enabled: false,
260
+ lookup_priority: 'catalog_first',
261
+ price_source_on_catalog_hit: 'barcode',
262
+ products: {
263
+ 414: pressUuid,
264
+ 419: pressUuid,
265
+ 434: pressUuid,
266
+ 439: pressUuid
267
+ }
268
+ }
269
+ }
270
+ })
271
+ t.ok(valid, ajv.errorsText(validateUpdatePayload.errors))
272
+ t.end()
273
+ })
274
+
275
+ test('Configurations: press_item_barcode accepts a prefix the client does not know', function (t) {
276
+ t.plan(1)
277
+ const valid = validateUpdatePayload({
278
+ features: {
279
+ press_item_barcode: { enabled: true, products: { 444: pressUuid } }
280
+ }
281
+ })
282
+ t.ok(valid, 'the supported prefixes are the map keys, so a new one needs no schema change')
283
+ t.end()
284
+ })
285
+
286
+ test('Configurations: press_item_barcode accepts barcode_first with a catalog-hit price source', function (t) {
287
+ t.plan(1)
288
+ const valid = validateUpdatePayload({
289
+ features: {
290
+ press_item_barcode: {
291
+ lookup_priority: 'barcode_first',
292
+ price_source_on_catalog_hit: 'catalog'
293
+ }
294
+ }
295
+ })
296
+ t.ok(valid, 'the combination is inert rather than invalid and must not be rejected')
297
+ t.end()
298
+ })
299
+
300
+ test('Configurations: press_item_barcode accepts an empty products map', function (t) {
301
+ t.plan(1)
302
+ const valid = validateUpdatePayload({
303
+ features: {
304
+ press_item_barcode: { enabled: true, products: {} }
305
+ }
306
+ })
307
+ t.ok(valid, 'equivalent to disabled, which the client resolves rather than the schema')
308
+ t.end()
309
+ })
310
+
311
+ test('Configurations: press_item_barcode rejects a prefix that is not exactly three digits', function (t) {
312
+ t.plan(2)
313
+ t.notOk(validateUpdatePayload({
314
+ features: { press_item_barcode: { products: { 41: pressUuid } } }
315
+ }), 'two digits')
316
+ t.notOk(validateUpdatePayload({
317
+ features: { press_item_barcode: { products: { 4141: pressUuid } } }
318
+ }), 'four digits')
319
+ t.end()
320
+ })
321
+
322
+ test('Configurations: press_item_barcode rejects a product value that is not a uuid', function (t) {
323
+ t.plan(1)
324
+ const valid = validateUpdatePayload({
325
+ features: { press_item_barcode: { products: { 414: 'presse-19' } } }
326
+ })
327
+ t.notOk(valid, 'the map holds article uuids, not names or numbers')
328
+ t.end()
329
+ })
330
+
331
+ test('Configurations: press_item_barcode rejects unknown enum values', function (t) {
332
+ t.plan(2)
333
+ t.notOk(validateUpdatePayload({
334
+ features: { press_item_barcode: { lookup_priority: 'catalogue_first' } }
335
+ }), 'lookup_priority')
336
+ t.notOk(validateUpdatePayload({
337
+ features: { press_item_barcode: { price_source_on_catalog_hit: 'ean' } }
338
+ }), 'price_source_on_catalog_hit')
339
+ t.end()
340
+ })