v3.12.0: the card preset carries the card's CAT (USAT) release

The release selector lives in the preset rather than a global chip: presets
already travel with the card (ICCID auto-selection, export/import), and the
operations that will consume the release are preset-aware.

- presets.py: the optional `release` field (4-18; empty = unspecified) joins
  PRESET_FIELDS; a value outside 4-18 is refused on save/import; the store
  schema version is 5.
- catrelease.py: DEFAULT_RELEASE = 12 and preset_release() - the effective
  release is the preset's own value, else Rel-12 (the safe level for cards
  whose release is not known; an unset release never filters anything away).
  The PWA mirrors both (CAT_RELEASE_DEFAULT + presetRelease()), drift-guarded.
- Cards tab: the editor's header row gains a compact CAT release select
  (unspecified / pre-Rel-6 stored as 5 / Rel-6 ... Rel-18); the empty option
  names the Rel-12 default and the tooltip explains the guard; the field is
  collected, cleared and restored with the other form fields.
- Nothing filters yet: the Simulator's event parameters (stage 3) will use
  the effective release.
- docs/api.md + help EN/RU updated; sw cache simple-v368.
This commit is contained in:
2026-10-02 11:28:33 +03:00
parent 2bb1528ca9
commit 3660a7e173
13 changed files with 149 additions and 13 deletions
+14
View File
@@ -32,6 +32,8 @@ function extractFunc(src, name) {
eval(extractBlock('const CAT_RELEASE = {', '// The display label of an introduction release')
.replace(/^const /gm, 'var '));
eval(extractFunc(html, 'catReleaseLabel'));
eval(extractBlock('const CAT_RELEASE_DEFAULT', 'function presetRelease').replace(/^const /gm, 'var '));
eval(extractFunc(html, 'presetRelease'));
eval(extractBlock('const EVENT_NAMES = {', 'const REJECTION_CAUSES = [').replace(/^const /gm, 'var '));
test('CAT_RELEASE carries the introduction releases of the event list', () => {
@@ -77,3 +79,15 @@ test('catReleaseLabel collapses everything below Rel-6', () => {
assert.strictEqual(catReleaseLabel(6), 'Rel-6');
assert.strictEqual(catReleaseLabel(18), 'Rel-18');
});
test('presetRelease resolves unspecified presets to the default', () => {
assert.strictEqual(CAT_RELEASE_DEFAULT, 12);
assert.strictEqual(presetRelease({ release: '14' }), 14);
assert.strictEqual(presetRelease({ release: 4 }), 4);
assert.strictEqual(presetRelease({ release: '18' }), 18);
// an unset or invalid release must never filter anything away
for (const p of [{}, { release: '' }, { release: '0' }, { release: '3' },
{ release: '99' }, { release: 'x' }, null, undefined]) {
assert.strictEqual(presetRelease(p), CAT_RELEASE_DEFAULT, JSON.stringify(p));
}
});