@ibiliaze/stringman 3.15.0 → 3.16.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.
- package/dist/ticket.d.ts +18 -19
- package/dist/ticket.js +49 -27
- package/package.json +2 -2
- package/src/ticket.ts +53 -28
package/dist/ticket.d.ts
CHANGED
|
@@ -5,36 +5,35 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const luhn10: (digits: string) => string;
|
|
7
7
|
/**
|
|
8
|
-
* Compact, stable
|
|
9
|
-
* Uses a
|
|
8
|
+
* Compact, stable numeric block from a short venue code.
|
|
9
|
+
* Uses the first two bytes of a SHA-1 digest modulo 10^len.
|
|
10
10
|
*/
|
|
11
|
-
export declare const venueBlockNum: (venueCode: string, len?:
|
|
11
|
+
export declare const venueBlockNum: (venueCode: string, len?: 3) => string;
|
|
12
12
|
/**
|
|
13
|
-
* Compact, stable
|
|
14
|
-
* If you pass your DB fixtureId here, you’ll get a short numeric block.
|
|
13
|
+
* Compact, stable numeric block from a Fixture/Match ID string.
|
|
15
14
|
*/
|
|
16
|
-
export declare const fixtureIdBlockNum: (fixtureId: string, len?:
|
|
15
|
+
export declare const fixtureIdBlockNum: (fixtureId: string, len?: 3) => string;
|
|
17
16
|
/**
|
|
18
|
-
* Compact, stable
|
|
17
|
+
* Compact, stable numeric block for a specific seat (section + row + col).
|
|
19
18
|
*/
|
|
20
|
-
export declare const seatBlockNum: (sectionId: string, row: number, col: number, len?:
|
|
19
|
+
export declare const seatBlockNum: (sectionId: string, row: number, col: number, len?: 3) => string;
|
|
21
20
|
/**
|
|
22
|
-
*
|
|
21
|
+
* 4-digit date/time block derived from local start time.
|
|
23
22
|
* Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
|
|
24
23
|
* Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
|
|
25
24
|
*/
|
|
26
|
-
export declare const fixtureBlockNum: (fixtureLocalIso: string, len?:
|
|
25
|
+
export declare const fixtureBlockNum: (fixtureLocalIso: string, len?: 4) => string;
|
|
27
26
|
/**
|
|
28
27
|
* Cryptographically strong random numeric block of the given length.
|
|
29
28
|
*/
|
|
30
|
-
export declare const rndBlockNum: (len?:
|
|
29
|
+
export declare const rndBlockNum: (len?: 6) => string;
|
|
31
30
|
/**
|
|
32
31
|
* Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
|
|
33
|
-
* Random (
|
|
32
|
+
* Random (6) + Seat (3) + Date/Time (4) + Fixture/Match ID (3) + Luhn(1)
|
|
34
33
|
*
|
|
35
|
-
* Default dashed display groups:
|
|
34
|
+
* Default dashed display groups: 6-3-4-3-1
|
|
36
35
|
*
|
|
37
|
-
* Lengths are chosen to keep the overall size at
|
|
36
|
+
* Lengths are chosen to keep the overall size at 17 digits (incl. Luhn),
|
|
38
37
|
* which plays nicely with scanners and manual entry.
|
|
39
38
|
*/
|
|
40
39
|
export declare const buildTicketCodeNumeric: (args: {
|
|
@@ -48,16 +47,16 @@ export declare const buildTicketCodeNumeric: (args: {
|
|
|
48
47
|
row: number;
|
|
49
48
|
/** Seat column/number. */
|
|
50
49
|
col: number;
|
|
51
|
-
/** Optional: your DB fixtureId. If present, becomes the
|
|
50
|
+
/** Optional: your DB fixtureId. If present, becomes the 3-digit “Fixture/Match ID” block. */
|
|
52
51
|
fixtureId?: string;
|
|
53
|
-
/**
|
|
52
|
+
/** Optional override for random block length (defaults to 6). */
|
|
54
53
|
rndLen?: number;
|
|
55
54
|
/** Show dashes for readability (default true). */
|
|
56
55
|
dashed?: boolean;
|
|
57
56
|
}) => string;
|
|
58
57
|
/**
|
|
59
58
|
* Validate a code built by buildTicketCodeNumeric (dashes optional).
|
|
60
|
-
* Checks: numeric, correct total length (
|
|
59
|
+
* Checks: numeric, correct total length (17), and Luhn checksum.
|
|
61
60
|
*/
|
|
62
61
|
export declare const validateTicketCodeNumeric: (code: string) => boolean;
|
|
63
62
|
/**
|
|
@@ -80,9 +79,9 @@ export declare const buildTicketCode: (args: {
|
|
|
80
79
|
row: number;
|
|
81
80
|
/** Seat column/number. */
|
|
82
81
|
col: number;
|
|
83
|
-
/** Optional: your DB fixtureId. If present, becomes the
|
|
82
|
+
/** Optional: your DB fixtureId. If present, becomes the 3-digit “Fixture/Match ID” block. */
|
|
84
83
|
fixtureId?: string;
|
|
85
|
-
/**
|
|
84
|
+
/** Optional override for random block length (defaults to 6). */
|
|
86
85
|
rndLen?: number;
|
|
87
86
|
/** Show dashes for readability (default true). */
|
|
88
87
|
dashed?: boolean;
|
package/dist/ticket.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// =====================================================================================
|
|
3
|
+
// Ticket Code Utilities — Format: 6-3-4-3-1 (Random • Seat • Date/Time • Fixture • Luhn)
|
|
4
|
+
// Total length = 17 digits (including Luhn). Dashes optional for readability.
|
|
5
|
+
// =====================================================================================
|
|
2
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
7
|
exports.validateTicketCode = exports.buildTicketCode = exports.getTicketId = exports.validateTicketCodeNumeric = exports.buildTicketCodeNumeric = exports.rndBlockNum = exports.fixtureBlockNum = exports.seatBlockNum = exports.fixtureIdBlockNum = exports.venueBlockNum = exports.luhn10 = void 0;
|
|
4
8
|
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
// ---------- config: block lengths (easy to tweak in one place) ----------
|
|
10
|
+
const CODE_LEN = {
|
|
11
|
+
rnd: 6, // Random block
|
|
12
|
+
seat: 3, // Seat identity (section + row + col hashed to 3 digits)
|
|
13
|
+
date: 4, // Fixture local datetime compacted to 4 digits
|
|
14
|
+
fix: 3, // Fixture/Match ID block
|
|
15
|
+
check: 1, // Luhn check digit
|
|
16
|
+
};
|
|
17
|
+
const EXPECTED_TOTAL_LEN = CODE_LEN.rnd + CODE_LEN.seat + CODE_LEN.date + CODE_LEN.fix + CODE_LEN.check; // 17
|
|
18
|
+
// =====================================================================================
|
|
19
|
+
// Helpers
|
|
20
|
+
// =====================================================================================
|
|
5
21
|
/**
|
|
6
22
|
* Left-pad a positive integer with zeros to a fixed width.
|
|
7
23
|
*/
|
|
@@ -29,10 +45,10 @@ const luhn10 = (digits) => {
|
|
|
29
45
|
};
|
|
30
46
|
exports.luhn10 = luhn10;
|
|
31
47
|
/**
|
|
32
|
-
* Compact, stable
|
|
33
|
-
* Uses a
|
|
48
|
+
* Compact, stable numeric block from a short venue code.
|
|
49
|
+
* Uses the first two bytes of a SHA-1 digest modulo 10^len.
|
|
34
50
|
*/
|
|
35
|
-
const venueBlockNum = (venueCode, len =
|
|
51
|
+
const venueBlockNum = (venueCode, len = CODE_LEN.fix) => {
|
|
36
52
|
try {
|
|
37
53
|
const h = (0, node_crypto_1.createHash)('sha1').update(venueCode.toUpperCase()).digest();
|
|
38
54
|
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
@@ -44,10 +60,9 @@ const venueBlockNum = (venueCode, len = 2) => {
|
|
|
44
60
|
};
|
|
45
61
|
exports.venueBlockNum = venueBlockNum;
|
|
46
62
|
/**
|
|
47
|
-
* Compact, stable
|
|
48
|
-
* If you pass your DB fixtureId here, you’ll get a short numeric block.
|
|
63
|
+
* Compact, stable numeric block from a Fixture/Match ID string.
|
|
49
64
|
*/
|
|
50
|
-
const fixtureIdBlockNum = (fixtureId, len =
|
|
65
|
+
const fixtureIdBlockNum = (fixtureId, len = CODE_LEN.fix) => {
|
|
51
66
|
try {
|
|
52
67
|
const h = (0, node_crypto_1.createHash)('sha1').update(fixtureId).digest();
|
|
53
68
|
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
@@ -59,9 +74,9 @@ const fixtureIdBlockNum = (fixtureId, len = 2) => {
|
|
|
59
74
|
};
|
|
60
75
|
exports.fixtureIdBlockNum = fixtureIdBlockNum;
|
|
61
76
|
/**
|
|
62
|
-
* Compact, stable
|
|
77
|
+
* Compact, stable numeric block for a specific seat (section + row + col).
|
|
63
78
|
*/
|
|
64
|
-
const seatBlockNum = (sectionId, row, col, len =
|
|
79
|
+
const seatBlockNum = (sectionId, row, col, len = CODE_LEN.seat) => {
|
|
65
80
|
try {
|
|
66
81
|
const h = (0, node_crypto_1.createHash)('sha1').update(`${sectionId}:${row}:${col}`).digest();
|
|
67
82
|
const n16 = (h[0] << 8) | h[1];
|
|
@@ -74,18 +89,18 @@ const seatBlockNum = (sectionId, row, col, len = 3) => {
|
|
|
74
89
|
};
|
|
75
90
|
exports.seatBlockNum = seatBlockNum;
|
|
76
91
|
/**
|
|
77
|
-
*
|
|
92
|
+
* 4-digit date/time block derived from local start time.
|
|
78
93
|
* Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
|
|
79
94
|
* Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
|
|
80
95
|
*/
|
|
81
|
-
const fixtureBlockNum = (fixtureLocalIso, len =
|
|
96
|
+
const fixtureBlockNum = (fixtureLocalIso, len = CODE_LEN.date) => {
|
|
82
97
|
try {
|
|
83
98
|
const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
|
|
84
99
|
if (!m)
|
|
85
100
|
throw new Error('Bad fixtureLocalIso');
|
|
86
101
|
const [_, y, M, d, h, mnt] = m;
|
|
87
102
|
const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
|
|
88
|
-
const n = compact % 10 ** len;
|
|
103
|
+
const n = compact % 10 ** len; // now 4 digits by default
|
|
89
104
|
return pad(n, len);
|
|
90
105
|
}
|
|
91
106
|
catch {
|
|
@@ -96,7 +111,7 @@ exports.fixtureBlockNum = fixtureBlockNum;
|
|
|
96
111
|
/**
|
|
97
112
|
* Cryptographically strong random numeric block of the given length.
|
|
98
113
|
*/
|
|
99
|
-
const rndBlockNum = (len =
|
|
114
|
+
const rndBlockNum = (len = CODE_LEN.rnd) => {
|
|
100
115
|
try {
|
|
101
116
|
const max = 10 ** len;
|
|
102
117
|
return pad((0, node_crypto_1.randomInt)(0, max), len);
|
|
@@ -106,30 +121,33 @@ const rndBlockNum = (len = 3) => {
|
|
|
106
121
|
}
|
|
107
122
|
};
|
|
108
123
|
exports.rndBlockNum = rndBlockNum;
|
|
124
|
+
// =====================================================================================
|
|
109
125
|
/**
|
|
110
126
|
* Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
|
|
111
|
-
* Random (
|
|
127
|
+
* Random (6) + Seat (3) + Date/Time (4) + Fixture/Match ID (3) + Luhn(1)
|
|
112
128
|
*
|
|
113
|
-
* Default dashed display groups:
|
|
129
|
+
* Default dashed display groups: 6-3-4-3-1
|
|
114
130
|
*
|
|
115
|
-
* Lengths are chosen to keep the overall size at
|
|
131
|
+
* Lengths are chosen to keep the overall size at 17 digits (incl. Luhn),
|
|
116
132
|
* which plays nicely with scanners and manual entry.
|
|
117
133
|
*/
|
|
118
134
|
const buildTicketCodeNumeric = (args) => {
|
|
119
135
|
try {
|
|
120
|
-
// --- individual blocks ---
|
|
121
|
-
const R = (0, exports.rndBlockNum)(
|
|
122
|
-
const S = (0, exports.seatBlockNum)(args.sectionId, args.row, args.col); // Seat (3)
|
|
123
|
-
const D = (0, exports.fixtureBlockNum)(args.fixtureLocalIso,
|
|
124
|
-
// Fixture/Match ID (
|
|
125
|
-
const F = args.fixtureId
|
|
136
|
+
// --- individual blocks (new lengths) ---
|
|
137
|
+
const R = (0, exports.rndBlockNum)(6); // Random (6)
|
|
138
|
+
const S = (0, exports.seatBlockNum)(args.sectionId, args.row, args.col, CODE_LEN.seat); // Seat (3)
|
|
139
|
+
const D = (0, exports.fixtureBlockNum)(args.fixtureLocalIso, CODE_LEN.date); // Date/Time (4)
|
|
140
|
+
// Fixture/Match ID (3): prefer fixtureId; fallback to venue code hash (also 3)
|
|
141
|
+
const F = args.fixtureId
|
|
142
|
+
? (0, exports.fixtureIdBlockNum)(args.fixtureId, CODE_LEN.fix)
|
|
143
|
+
: (0, exports.venueBlockNum)(args.venueCode, CODE_LEN.fix);
|
|
126
144
|
// --- assemble (REVERSED order vs the picture’s left-to-right) ---
|
|
127
|
-
const body = `${R}${S}${D}${F}`; //
|
|
145
|
+
const body = `${R}${S}${D}${F}`; // 6+3+4+3 = 16
|
|
128
146
|
const C = (0, exports.luhn10)(body); // 1 digit
|
|
129
|
-
const numeric = `${body}${C}`; // total
|
|
147
|
+
const numeric = `${body}${C}`; // total 17 digits
|
|
130
148
|
if (args.dashed === false)
|
|
131
149
|
return numeric;
|
|
132
|
-
// Readability grouping for scanners/ushers:
|
|
150
|
+
// Readability grouping for scanners/ushers: 6-3-4-3-1
|
|
133
151
|
return `${R}-${S}-${D}-${F}-${C}`;
|
|
134
152
|
}
|
|
135
153
|
catch {
|
|
@@ -137,15 +155,18 @@ const buildTicketCodeNumeric = (args) => {
|
|
|
137
155
|
}
|
|
138
156
|
};
|
|
139
157
|
exports.buildTicketCodeNumeric = buildTicketCodeNumeric;
|
|
158
|
+
// =====================================================================================
|
|
140
159
|
/**
|
|
141
160
|
* Validate a code built by buildTicketCodeNumeric (dashes optional).
|
|
142
|
-
* Checks: numeric, correct total length (
|
|
161
|
+
* Checks: numeric, correct total length (17), and Luhn checksum.
|
|
143
162
|
*/
|
|
144
163
|
const validateTicketCodeNumeric = (code) => {
|
|
145
164
|
try {
|
|
146
165
|
const clean = code.replace(/-/g, '');
|
|
147
|
-
if (!/^\d
|
|
148
|
-
return false;
|
|
166
|
+
if (!/^\d+$/.test(clean))
|
|
167
|
+
return false;
|
|
168
|
+
if (clean.length !== EXPECTED_TOTAL_LEN)
|
|
169
|
+
return false; // 17
|
|
149
170
|
const body = clean.slice(0, -1);
|
|
150
171
|
const check = clean.slice(-1);
|
|
151
172
|
return (0, exports.luhn10)(body) === check;
|
|
@@ -155,6 +176,7 @@ const validateTicketCodeNumeric = (code) => {
|
|
|
155
176
|
}
|
|
156
177
|
};
|
|
157
178
|
exports.validateTicketCodeNumeric = validateTicketCodeNumeric;
|
|
179
|
+
// =====================================================================================
|
|
158
180
|
/**
|
|
159
181
|
* A stable, human-readable internal ID for a seat within a fixture.
|
|
160
182
|
* (Not used in the printed/scanned code — just handy elsewhere.)
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ibiliaze/stringman",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"pub": "npm publish --access public",
|
|
10
|
-
"git": "git add .; git commit -m 'changes'; git tag -a
|
|
10
|
+
"git": "git add .; git commit -m 'changes'; git tag -a 3.16.0 -m '3.16.0'; git push origin 3.16.0; git push",
|
|
11
11
|
"push": "npm run build; npm run git; npm run pub"
|
|
12
12
|
},
|
|
13
13
|
"author": "Ibi Hasanli",
|
package/src/ticket.ts
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
// =====================================================================================
|
|
2
|
+
// Ticket Code Utilities — Format: 6-3-4-3-1 (Random • Seat • Date/Time • Fixture • Luhn)
|
|
3
|
+
// Total length = 17 digits (including Luhn). Dashes optional for readability.
|
|
4
|
+
// =====================================================================================
|
|
5
|
+
|
|
1
6
|
import { createHash, randomInt } from 'node:crypto';
|
|
2
7
|
|
|
8
|
+
// ---------- config: block lengths (easy to tweak in one place) ----------
|
|
9
|
+
const CODE_LEN = {
|
|
10
|
+
rnd: 6, // Random block
|
|
11
|
+
seat: 3, // Seat identity (section + row + col hashed to 3 digits)
|
|
12
|
+
date: 4, // Fixture local datetime compacted to 4 digits
|
|
13
|
+
fix: 3, // Fixture/Match ID block
|
|
14
|
+
check: 1, // Luhn check digit
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
const EXPECTED_TOTAL_LEN = CODE_LEN.rnd + CODE_LEN.seat + CODE_LEN.date + CODE_LEN.fix + CODE_LEN.check; // 17
|
|
18
|
+
|
|
19
|
+
// =====================================================================================
|
|
20
|
+
// Helpers
|
|
21
|
+
// =====================================================================================
|
|
22
|
+
|
|
3
23
|
/**
|
|
4
24
|
* Left-pad a positive integer with zeros to a fixed width.
|
|
5
25
|
*/
|
|
@@ -27,10 +47,10 @@ export const luhn10 = (digits: string): string => {
|
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
/**
|
|
30
|
-
* Compact, stable
|
|
31
|
-
* Uses a
|
|
50
|
+
* Compact, stable numeric block from a short venue code.
|
|
51
|
+
* Uses the first two bytes of a SHA-1 digest modulo 10^len.
|
|
32
52
|
*/
|
|
33
|
-
export const venueBlockNum = (venueCode: string, len =
|
|
53
|
+
export const venueBlockNum = (venueCode: string, len = CODE_LEN.fix) => {
|
|
34
54
|
try {
|
|
35
55
|
const h = createHash('sha1').update(venueCode.toUpperCase()).digest();
|
|
36
56
|
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
@@ -41,10 +61,9 @@ export const venueBlockNum = (venueCode: string, len = 2) => {
|
|
|
41
61
|
};
|
|
42
62
|
|
|
43
63
|
/**
|
|
44
|
-
* Compact, stable
|
|
45
|
-
* If you pass your DB fixtureId here, you’ll get a short numeric block.
|
|
64
|
+
* Compact, stable numeric block from a Fixture/Match ID string.
|
|
46
65
|
*/
|
|
47
|
-
export const fixtureIdBlockNum = (fixtureId: string, len =
|
|
66
|
+
export const fixtureIdBlockNum = (fixtureId: string, len = CODE_LEN.fix) => {
|
|
48
67
|
try {
|
|
49
68
|
const h = createHash('sha1').update(fixtureId).digest();
|
|
50
69
|
const n = ((h[0] << 8) | h[1]) % 10 ** len;
|
|
@@ -55,9 +74,9 @@ export const fixtureIdBlockNum = (fixtureId: string, len = 2) => {
|
|
|
55
74
|
};
|
|
56
75
|
|
|
57
76
|
/**
|
|
58
|
-
* Compact, stable
|
|
77
|
+
* Compact, stable numeric block for a specific seat (section + row + col).
|
|
59
78
|
*/
|
|
60
|
-
export const seatBlockNum = (sectionId: string, row: number, col: number, len =
|
|
79
|
+
export const seatBlockNum = (sectionId: string, row: number, col: number, len = CODE_LEN.seat) => {
|
|
61
80
|
try {
|
|
62
81
|
const h = createHash('sha1').update(`${sectionId}:${row}:${col}`).digest();
|
|
63
82
|
const n16 = (h[0] << 8) | h[1];
|
|
@@ -69,17 +88,17 @@ export const seatBlockNum = (sectionId: string, row: number, col: number, len =
|
|
|
69
88
|
};
|
|
70
89
|
|
|
71
90
|
/**
|
|
72
|
-
*
|
|
91
|
+
* 4-digit date/time block derived from local start time.
|
|
73
92
|
* Input format expected: "YYYY-MM-DDThh:mm" (local, no 'Z').
|
|
74
93
|
* Encodes YYMMDDhhmm then reduces modulo 10^len to keep width stable.
|
|
75
94
|
*/
|
|
76
|
-
export const fixtureBlockNum = (fixtureLocalIso: string, len =
|
|
95
|
+
export const fixtureBlockNum = (fixtureLocalIso: string, len = CODE_LEN.date) => {
|
|
77
96
|
try {
|
|
78
97
|
const m = fixtureLocalIso.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})/);
|
|
79
98
|
if (!m) throw new Error('Bad fixtureLocalIso');
|
|
80
99
|
const [_, y, M, d, h, mnt] = m;
|
|
81
100
|
const compact = Number(`${y.slice(2)}${M}${d}${h}${mnt}`); // e.g. 2510312000
|
|
82
|
-
const n = compact % 10 ** len;
|
|
101
|
+
const n = compact % 10 ** len; // now 4 digits by default
|
|
83
102
|
return pad(n, len);
|
|
84
103
|
} catch {
|
|
85
104
|
return ''.padStart(len, '0');
|
|
@@ -89,7 +108,7 @@ export const fixtureBlockNum = (fixtureLocalIso: string, len = 6) => {
|
|
|
89
108
|
/**
|
|
90
109
|
* Cryptographically strong random numeric block of the given length.
|
|
91
110
|
*/
|
|
92
|
-
export const rndBlockNum = (len =
|
|
111
|
+
export const rndBlockNum = (len = CODE_LEN.rnd) => {
|
|
93
112
|
try {
|
|
94
113
|
const max = 10 ** len;
|
|
95
114
|
return pad(randomInt(0, max), len);
|
|
@@ -98,13 +117,14 @@ export const rndBlockNum = (len = 3) => {
|
|
|
98
117
|
}
|
|
99
118
|
};
|
|
100
119
|
|
|
120
|
+
// =====================================================================================
|
|
101
121
|
/**
|
|
102
122
|
* Build a numeric ticket code with blocks ordered to match the picture *in reverse*:
|
|
103
|
-
* Random (
|
|
123
|
+
* Random (6) + Seat (3) + Date/Time (4) + Fixture/Match ID (3) + Luhn(1)
|
|
104
124
|
*
|
|
105
|
-
* Default dashed display groups:
|
|
125
|
+
* Default dashed display groups: 6-3-4-3-1
|
|
106
126
|
*
|
|
107
|
-
* Lengths are chosen to keep the overall size at
|
|
127
|
+
* Lengths are chosen to keep the overall size at 17 digits (incl. Luhn),
|
|
108
128
|
* which plays nicely with scanners and manual entry.
|
|
109
129
|
*/
|
|
110
130
|
export const buildTicketCodeNumeric = (args: {
|
|
@@ -118,43 +138,47 @@ export const buildTicketCodeNumeric = (args: {
|
|
|
118
138
|
row: number;
|
|
119
139
|
/** Seat column/number. */
|
|
120
140
|
col: number;
|
|
121
|
-
/** Optional: your DB fixtureId. If present, becomes the
|
|
141
|
+
/** Optional: your DB fixtureId. If present, becomes the 3-digit “Fixture/Match ID” block. */
|
|
122
142
|
fixtureId?: string;
|
|
123
|
-
/**
|
|
143
|
+
/** Optional override for random block length (defaults to 6). */
|
|
124
144
|
rndLen?: number;
|
|
125
145
|
/** Show dashes for readability (default true). */
|
|
126
146
|
dashed?: boolean;
|
|
127
147
|
}) => {
|
|
128
148
|
try {
|
|
129
|
-
// --- individual blocks ---
|
|
130
|
-
const R = rndBlockNum(
|
|
131
|
-
const S = seatBlockNum(args.sectionId, args.row, args.col); // Seat (3)
|
|
132
|
-
const D = fixtureBlockNum(args.fixtureLocalIso,
|
|
133
|
-
// Fixture/Match ID (
|
|
134
|
-
const F = args.fixtureId
|
|
149
|
+
// --- individual blocks (new lengths) ---
|
|
150
|
+
const R = rndBlockNum(6); // Random (6)
|
|
151
|
+
const S = seatBlockNum(args.sectionId, args.row, args.col, CODE_LEN.seat); // Seat (3)
|
|
152
|
+
const D = fixtureBlockNum(args.fixtureLocalIso, CODE_LEN.date); // Date/Time (4)
|
|
153
|
+
// Fixture/Match ID (3): prefer fixtureId; fallback to venue code hash (also 3)
|
|
154
|
+
const F = args.fixtureId
|
|
155
|
+
? fixtureIdBlockNum(args.fixtureId, CODE_LEN.fix)
|
|
156
|
+
: venueBlockNum(args.venueCode, CODE_LEN.fix);
|
|
135
157
|
|
|
136
158
|
// --- assemble (REVERSED order vs the picture’s left-to-right) ---
|
|
137
|
-
const body = `${R}${S}${D}${F}`; //
|
|
159
|
+
const body = `${R}${S}${D}${F}`; // 6+3+4+3 = 16
|
|
138
160
|
const C = luhn10(body); // 1 digit
|
|
139
|
-
const numeric = `${body}${C}`; // total
|
|
161
|
+
const numeric = `${body}${C}`; // total 17 digits
|
|
140
162
|
|
|
141
163
|
if (args.dashed === false) return numeric;
|
|
142
164
|
|
|
143
|
-
// Readability grouping for scanners/ushers:
|
|
165
|
+
// Readability grouping for scanners/ushers: 6-3-4-3-1
|
|
144
166
|
return `${R}-${S}-${D}-${F}-${C}`;
|
|
145
167
|
} catch {
|
|
146
168
|
return '';
|
|
147
169
|
}
|
|
148
170
|
};
|
|
149
171
|
|
|
172
|
+
// =====================================================================================
|
|
150
173
|
/**
|
|
151
174
|
* Validate a code built by buildTicketCodeNumeric (dashes optional).
|
|
152
|
-
* Checks: numeric, correct total length (
|
|
175
|
+
* Checks: numeric, correct total length (17), and Luhn checksum.
|
|
153
176
|
*/
|
|
154
177
|
export const validateTicketCodeNumeric = (code: string) => {
|
|
155
178
|
try {
|
|
156
179
|
const clean = code.replace(/-/g, '');
|
|
157
|
-
if (!/^\d
|
|
180
|
+
if (!/^\d+$/.test(clean)) return false;
|
|
181
|
+
if (clean.length !== EXPECTED_TOTAL_LEN) return false; // 17
|
|
158
182
|
const body = clean.slice(0, -1);
|
|
159
183
|
const check = clean.slice(-1);
|
|
160
184
|
return luhn10(body) === check;
|
|
@@ -163,6 +187,7 @@ export const validateTicketCodeNumeric = (code: string) => {
|
|
|
163
187
|
}
|
|
164
188
|
};
|
|
165
189
|
|
|
190
|
+
// =====================================================================================
|
|
166
191
|
/**
|
|
167
192
|
* A stable, human-readable internal ID for a seat within a fixture.
|
|
168
193
|
* (Not used in the printed/scanned code — just handy elsewhere.)
|