Skip to content

Commit 35aae5b

Browse files
vyruz1986bchavez
andauthored
Added extension method on Person to generate Belgian national number (#557)
* feat: Added extension method on Person to generate Belgian national number * Add README.md API extension. --------- Co-authored-by: Brian Chavez <[email protected]>
1 parent 070a9dc commit 35aae5b

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ In the examples above, all three alternative styles of using **Bogus** produce t
485485
* `WeightedRandom<T>` - Returns a selection of T[] based on a weighted distribution of probability.
486486

487487
#### API Extension Methods
488+
* **`using Bogus.Extensions.Belgium;`**
489+
* `Bogus.Person.NationalNumber()` - Rijksregisternummer / Numéro Nationale
488490
* **`using Bogus.Extensions.Brazil;`**
489491
* `Bogus.Person.Cpf()` - Cadastro de Pessoas Físicas
490492
* `Bogus.DataSets.Company.Cnpj()` - Cadastro Nacional da Pessoa Jurídica
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using Bogus.DataSets;
2+
using Bogus.Extensions.Belgium;
3+
using FluentAssertions;
4+
using Xunit;
5+
6+
namespace Bogus.Tests.ExtensionTests;
7+
8+
public class BelgianExtensionTests : SeededTest
9+
{
10+
private readonly Faker _faker;
11+
12+
public BelgianExtensionTests()
13+
{
14+
_faker = new Faker();
15+
}
16+
17+
[Fact]
18+
public void can_generate_national_number_for_belgium()
19+
{
20+
// Act
21+
var obtained = _faker.Person.NationalNumber(includeFormatSymbols: false);
22+
23+
obtained.Dump();
24+
25+
// Assert
26+
obtained.Should().NotBeNullOrWhiteSpace();
27+
ShouldBeLegalBelgianNationalNumber(obtained);
28+
ShouldBeCorrectGenderCode(_faker.Person.Gender, obtained);
29+
ShouldHaveCorrectChecksum(obtained);
30+
}
31+
32+
[Fact]
33+
public void excludes_formatting_national_number()
34+
{
35+
var result = _faker.Person.NationalNumber(includeFormatSymbols: false);
36+
result.Should().NotContainAny("-", ".");
37+
}
38+
39+
[Fact]
40+
public void includes_formatting_national_number()
41+
{
42+
var result = _faker.Person.NationalNumber(includeFormatSymbols: true);
43+
result.Should().ContainAll("-", ".");
44+
}
45+
46+
47+
private void ShouldHaveCorrectChecksum(string candidate)
48+
{
49+
var baseNumber = long.Parse(candidate.Substring(0, 9));
50+
var checkNumber = candidate.Substring(9);
51+
var birthYear = int.Parse(candidate.Substring(0, 2));
52+
53+
if (birthYear == 00)
54+
baseNumber += 2000000000L;
55+
56+
var expectedCheckNumber = 97 - (int)(baseNumber % 97);
57+
58+
checkNumber.Should().Be(expectedCheckNumber.ToString());
59+
}
60+
61+
private void ShouldBeLegalBelgianNationalNumber(string candidate)
62+
{
63+
// Check if the first 6 digits represent a valid date.
64+
var year = int.Parse(candidate.Substring(0, 2));
65+
var month = int.Parse(candidate.Substring(2, 2));
66+
var day = int.Parse(candidate.Substring(4, 2));
67+
68+
day.Should().BeInRange(1, 31);
69+
month.Should().BeInRange(1, 12);
70+
year.Should().BeInRange(0, 99);
71+
}
72+
73+
private void ShouldBeCorrectGenderCode(Name.Gender gender, string candidate)
74+
{
75+
var sequenceNumber = int.Parse(candidate.Substring(6, 3));
76+
77+
if (gender == Name.Gender.Female)
78+
{
79+
sequenceNumber.Should()
80+
.Match(x => x % 2 == 0);
81+
}
82+
83+
if (gender == Name.Gender.Male)
84+
{
85+
sequenceNumber.Should()
86+
.Match(x => x % 2 == 1);
87+
}
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
namespace Bogus.Extensions.Belgium;
2+
3+
/// <summary>
4+
/// API extensions specific for a geographical location.
5+
/// </summary>
6+
public static class ExtensionsForBelgium
7+
{
8+
9+
/// <summary>
10+
/// The national number is a unique identification number (11 digits) assigned to persons registered in Belgium.
11+
/// Everyone with a Belgian identity document or a residence document has this number.
12+
/// </summary>
13+
/// <param name="includeFormatSymbols">Includes formatting symbols.</param>
14+
public static string NationalNumber(this Person p, bool includeFormatSymbols = true)
15+
{
16+
/*
17+
YY.MM.DD-SSS.CC
18+
| | | | |
19+
| | | | |
20+
| | | | |--> (C)Checksum digit (modulo 97)
21+
| | | |------> (S)Sequential number for people born on the same date. Even for women (002-998), odd for men (001-997).
22+
| | |---------> (D)Day of birth date (may be 0 for international refugees where exact birth date is unknown)
23+
| |------------> (M)Month of birth date (may be 0 for international refugees where exact birth date is unknown)
24+
|---------------> (Y)Year of birth date (last two digits)
25+
26+
https://nl.wikipedia.org/wiki/Rijksregisternummer
27+
*/
28+
29+
var sequence = p.Gender == DataSets.Name.Gender.Male
30+
? p.Random.Odd(1, 997)
31+
: p.Random.Even(2, 998);
32+
33+
var baseNumber = $"{p.DateOfBirth:yyMMdd}{sequence:000}";
34+
var baseNumberLong = ulong.Parse(baseNumber);
35+
36+
var bornAfter2000 = p.DateOfBirth.Year >= 2000;
37+
var checkNumber = bornAfter2000
38+
? 97 - (int)((baseNumberLong + 2000000000L) % 97)
39+
: 97 - (int)(baseNumberLong % 97);
40+
41+
var nationalNumber = $"{baseNumber}{checkNumber}";
42+
43+
return includeFormatSymbols
44+
? FormatNationalNumber(nationalNumber)
45+
: nationalNumber;
46+
}
47+
48+
private static string FormatNationalNumber(string nationalNumber)
49+
{
50+
string year = nationalNumber.Substring(0, 2);
51+
string month = nationalNumber.Substring(2, 2);
52+
string day = nationalNumber.Substring(4, 2);
53+
string serial = nationalNumber.Substring(6, 3);
54+
string checkDigits = nationalNumber.Substring(9, 2);
55+
56+
// Format as yy.MM.dd-sss.00
57+
return $"{year}.{month}.{day}-{serial}.{checkDigits}";
58+
}
59+
}

0 commit comments

Comments
 (0)