Skip to content

Commit 933ef62

Browse files
committed
Add two schemas for social insurance number
1 parent 46b68b8 commit 933ef62

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

src/lib/string.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ describe("string tests", () => {
126126
[undefined as unknown as string, false],
127127
["7561234567891", false],
128128
["7569217076985", true],
129+
["756.92170769.85", false],
129130
["756.9217.0769.85", true],
130131
["756..9217.0769.85", false],
131132
["756.1234.5678.91", false],

src/lib/string.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,45 +75,55 @@ export function truncate(value: string | undefined, maxLength: number, suffix =
7575
* @returns The result if the social insurance number is valid or not
7676
*/
7777
export function isValidSwissSocialSecurityNumber(socialInsuranceNumber: string): boolean {
78+
// 1. Check if input is empty or only whitespace
7879
if (isNullOrWhitespace(socialInsuranceNumber)) {
7980
return false;
8081
}
8182

82-
const socialInsuranceNumberWithDots = new RegExp(/^756\.?\d{4}\.?\d{4}\.?\d{2}$/);
83+
/**
84+
* 2. Check if input matches accepted formats:
85+
* - With dots: 756.XXXX.XXXX.XX
86+
* - Without dots: 756XXXXXXXXXX
87+
*/
88+
const socialInsuranceNumberWithDots = new RegExp(/^756\.\d{4}\.\d{4}\.\d{2}$/);
89+
const socialInsuranceNumberWithoutDots = new RegExp(/^756\d{10}$/);
8390

84-
if (!socialInsuranceNumberWithDots.test(socialInsuranceNumber)) {
91+
if (!socialInsuranceNumberWithDots.test(socialInsuranceNumber) && !socialInsuranceNumberWithoutDots.test(socialInsuranceNumber)) {
8592
return false;
8693
}
8794

95+
// 3. Remove all dots → get a string of 13 digits
96+
const compactNumber = socialInsuranceNumber.replaceAll(".", "");
97+
8898
/**
89-
* Validates a Swiss social security number (AHV number).
90-
*
91-
* Validation steps:
92-
* - The number must start with 756, be 13 digits long and follow one of the accepted formats:
93-
* - "756.XXXX.XXXX.XX" or "756XXXXXXXXXX".
94-
* - Remove dots → 13 digits remain.
95-
* - The last digit is the check digit.
96-
* - To calculate the check digit:
97-
* - Take the first 12 digits and reverse them.
98-
* - Multiply digits at even positions by 3, and digits at odd positions by 1.
99-
* - Sum all results.
100-
* - Look at the last digit of the sum (sum % 10).
101-
* - The check digit is the value needed to reach the next multiple of 10.
102-
* - The number is valid if this check digit matches the last digit.
99+
* 4. Separate digits for checksum calculation
100+
* - first 12 digits: used to calculate checksum
101+
* - last digit: actual check digit
103102
*/
104-
105-
const compactNumber = socialInsuranceNumber.replaceAll(".", "");
106103
const digits = compactNumber.slice(0, -1);
107104
const reversedDigits = [...digits].reverse().join("");
108105
const reversedDigitsArray = [...reversedDigits];
109106

107+
/*
108+
* 5. Calculate weighted sum for checksum
109+
* - Even positions (after reversing) ×3
110+
* - Odd positions ×1
111+
*/
110112
let sum = 0;
111113
for (const [i, element] of reversedDigitsArray.entries()) {
112114
sum += i % 2 === 0 ? Number(element) * 3 : Number(element) * 1;
113115
}
114116

117+
/*
118+
* 6. Calculate expected check digit
119+
* - Check digit = value to reach next multiple of 10
120+
*/
115121
const checksum = (10 - (sum % 10)) % 10;
116122
const checknumber = Number.parseInt(compactNumber.slice(-1));
117123

124+
/*
125+
* 7. Compare calculated check digit with actual last digit
126+
* - If equal → valid AHV number
127+
*/
118128
return checksum === checknumber;
119129
}

0 commit comments

Comments
 (0)