@@ -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 */
7777export 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 ( / ^ 7 5 6 \. ? \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 ( / ^ 7 5 6 \. \d { 4 } \. \d { 4 } \. \d { 2 } $ / ) ;
89+ const socialInsuranceNumberWithoutDots = new RegExp ( / ^ 7 5 6 \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