From 175f9ece5d5f24d99da6bc77ce1e7e0f1f3f5b65 Mon Sep 17 00:00:00 2001 From: Aulon Mujaj <4094284+aulonm@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:13:19 +0200 Subject: [PATCH 1/5] validation: add norwegian bank account number --- packages/validation/README.md | 13 +++++++++++++ packages/validation/src/no.ts | 6 ++++++ packages/validation/src/validation.test.ts | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/packages/validation/README.md b/packages/validation/README.md index d378576..c74d11c 100644 --- a/packages/validation/README.md +++ b/packages/validation/README.md @@ -157,6 +157,19 @@ import { validateObosMembershipNumber } from '@obosbbl/validation/se'; validateObosMembershipNumber('0000000') // => true ``` +### validateBankAccountNumber() + +Validates that the value is a valid organization number. Validates the checksum of the number. + +```js +// 🇳🇴 example +import { validateBankAccountNumber } from '@obosbbl/validation/no'; +validateBankAccountNumber('12345678903'); // => true + +// 🇸🇪 example +// TODO: implement +``` + ## Example usage with Zod diff --git a/packages/validation/src/no.ts b/packages/validation/src/no.ts index fe99f4c..8119cb0 100644 --- a/packages/validation/src/no.ts +++ b/packages/validation/src/no.ts @@ -159,3 +159,9 @@ export function validateNationalIdentityNumber( return isValidDate(year, month, day); } + +export function validateBankAccountNumber(value: string): boolean { + // Norwegian bank account numbers use mod 11 with one control digits. + // The first one is calculated for all 11 digits + return mod11(value, [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]); +} diff --git a/packages/validation/src/validation.test.ts b/packages/validation/src/validation.test.ts index ee8a8c5..f69481a 100644 --- a/packages/validation/src/validation.test.ts +++ b/packages/validation/src/validation.test.ts @@ -47,6 +47,16 @@ describe('no', () => { expect(no.validateOrganizationNumber(input, options)).toBe(expected); }); + test.each([ + ['12345678903', true], + ['12345678901', false], + ['9523 08 20059', false], + ['95230820052', false], + ['9523.08.20059', false], + ])('validateAccountNumber(%s) -> %s', (input, expected) => { + expect(no.validateBankAccountNumber(input)).toBe(expected); + }); + test.each([ ['1234567', true, undefined], // too short From bb29d3df3f2f5fbc750ee415a3b4c97176f98fb9 Mon Sep 17 00:00:00 2001 From: Aulon Mujaj <4094284+aulonm@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:24:35 +0200 Subject: [PATCH 2/5] changeset --- .changeset/tiny-bobcats-buy.md | 15 +++++++++++++++ packages/validation/src/no.ts | 2 +- packages/validation/src/validation.test.ts | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .changeset/tiny-bobcats-buy.md diff --git a/.changeset/tiny-bobcats-buy.md b/.changeset/tiny-bobcats-buy.md new file mode 100644 index 0000000..7fddcb1 --- /dev/null +++ b/.changeset/tiny-bobcats-buy.md @@ -0,0 +1,15 @@ +--- +"@obosbbl/validation": patch +--- + +add method `no/validateAccountNumber()` + +Validates that the input is a valid Norwegian national identity number (either a fødselsnummer or a D-nummer). +It validates the checksum and checks if the date of birth is valid. + +``` + import { validateAccountNumber } from "@obosbbl/validation/no"; + + validateAccountNumber('12345678903') // => true + +``` diff --git a/packages/validation/src/no.ts b/packages/validation/src/no.ts index 8119cb0..50478df 100644 --- a/packages/validation/src/no.ts +++ b/packages/validation/src/no.ts @@ -160,7 +160,7 @@ export function validateNationalIdentityNumber( return isValidDate(year, month, day); } -export function validateBankAccountNumber(value: string): boolean { +export function validateAccountNumber(value: string): boolean { // Norwegian bank account numbers use mod 11 with one control digits. // The first one is calculated for all 11 digits return mod11(value, [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]); diff --git a/packages/validation/src/validation.test.ts b/packages/validation/src/validation.test.ts index f69481a..8c3a3c5 100644 --- a/packages/validation/src/validation.test.ts +++ b/packages/validation/src/validation.test.ts @@ -54,7 +54,7 @@ describe('no', () => { ['95230820052', false], ['9523.08.20059', false], ])('validateAccountNumber(%s) -> %s', (input, expected) => { - expect(no.validateBankAccountNumber(input)).toBe(expected); + expect(no.validateAccountNumber(input)).toBe(expected); }); test.each([ From 849547400f62d44f3fc46711df2775c64cdcf64e Mon Sep 17 00:00:00 2001 From: Aulon Mujaj <4094284+aulonm@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:25:24 +0200 Subject: [PATCH 3/5] fix readme --- packages/validation/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/validation/README.md b/packages/validation/README.md index c74d11c..8118e24 100644 --- a/packages/validation/README.md +++ b/packages/validation/README.md @@ -163,8 +163,8 @@ Validates that the value is a valid organization number. Validates the checksum ```js // 🇳🇴 example -import { validateBankAccountNumber } from '@obosbbl/validation/no'; -validateBankAccountNumber('12345678903'); // => true +import { validateAccountNumber } from '@obosbbl/validation/no'; +validateAccountNumber('12345678903'); // => true // 🇸🇪 example // TODO: implement From 49079d86e0dc32de4dd5dab5432df528d3a104b5 Mon Sep 17 00:00:00 2001 From: Aulon Mujaj <4094284+aulonm@users.noreply.github.com> Date: Thu, 10 Apr 2025 08:58:52 +0200 Subject: [PATCH 4/5] QA fixes --- .changeset/tiny-bobcats-buy.md | 3 +-- packages/validation/README.md | 2 +- packages/validation/src/no.ts | 22 +++++++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.changeset/tiny-bobcats-buy.md b/.changeset/tiny-bobcats-buy.md index 7fddcb1..d3e3117 100644 --- a/.changeset/tiny-bobcats-buy.md +++ b/.changeset/tiny-bobcats-buy.md @@ -4,8 +4,7 @@ add method `no/validateAccountNumber()` -Validates that the input is a valid Norwegian national identity number (either a fødselsnummer or a D-nummer). -It validates the checksum and checks if the date of birth is valid. +Validates that the input is a valid Norwegian bank account number. ``` import { validateAccountNumber } from "@obosbbl/validation/no"; diff --git a/packages/validation/README.md b/packages/validation/README.md index 8118e24..93f1a90 100644 --- a/packages/validation/README.md +++ b/packages/validation/README.md @@ -157,7 +157,7 @@ import { validateObosMembershipNumber } from '@obosbbl/validation/se'; validateObosMembershipNumber('0000000') // => true ``` -### validateBankAccountNumber() +### validateAccountNumber() Validates that the value is a valid organization number. Validates the checksum of the number. diff --git a/packages/validation/src/no.ts b/packages/validation/src/no.ts index 50478df..85e5fee 100644 --- a/packages/validation/src/no.ts +++ b/packages/validation/src/no.ts @@ -160,7 +160,27 @@ export function validateNationalIdentityNumber( return isValidDate(year, month, day); } -export function validateAccountNumber(value: string): boolean { +type AccountNumberOptions = ValidatorOptions; + +/** + * Validates that the input value is a Norwegian bank account number (kontonummer). + * + * It validates the control digit. + * + * @example + * ``` + * validateAccountNumber('XXXXXXXXXXX') // => true + * ``` + */ +export function validateAccountNumber( + value: string, + options: AccountNumberOptions = {}, +): boolean { + if (options.allowFormatting) { + // biome-ignore lint/style/noParameterAssign: + value = stripFormatting(value); + } + // Norwegian bank account numbers use mod 11 with one control digits. // The first one is calculated for all 11 digits return mod11(value, [5, 4, 3, 2, 7, 6, 5, 4, 3, 2]); From 50396842a22c3d71483159c1cff4a9501660448a Mon Sep 17 00:00:00 2001 From: Aulon Mujaj <4094284+aulonm@users.noreply.github.com> Date: Thu, 10 Apr 2025 09:02:34 +0200 Subject: [PATCH 5/5] add tests for formatting --- packages/validation/src/validation.test.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/validation/src/validation.test.ts b/packages/validation/src/validation.test.ts index 8c3a3c5..cde2cc2 100644 --- a/packages/validation/src/validation.test.ts +++ b/packages/validation/src/validation.test.ts @@ -48,13 +48,18 @@ describe('no', () => { }); test.each([ - ['12345678903', true], - ['12345678901', false], - ['9523 08 20059', false], - ['95230820052', false], - ['9523.08.20059', false], - ])('validateAccountNumber(%s) -> %s', (input, expected) => { - expect(no.validateAccountNumber(input)).toBe(expected); + ['12345678903', true, undefined], + ['12345678901', false, undefined], + ['1234 56 78903', false, undefined], + ['95230820052', false, undefined], + ['9523.08.20059', false, undefined], + + ['1234 56 78903', true, { allowFormatting: true }], + ['1234 56 78903', false, { allowFormatting: false }], + ['9523.08.20059', true, { allowFormatting: true }], + ['9523.08.20059', false, { allowFormatting: false }], + ])('validateAccountNumber(%s) -> %s', (input, expected, options) => { + expect(no.validateAccountNumber(input, options)).toBe(expected); }); test.each([