File Coverage

blib/lib/Business/DK/CPR.pm
Criterion Covered Total %
statement 138 190 72.6
branch 27 58 46.5
condition 5 15 33.3
subroutine 34 34 100.0
pod 9 9 100.0
total 213 306 69.6


line stmt bran cond sub pod time code
1             package Business::DK::CPR;
2              
3 13     13   116190 use strict;
  13         65  
  13         424  
4 13     13   69 use warnings;
  13         24  
  13         378  
5 13     13   65 use Carp qw(croak carp);
  13         26  
  13         715  
6 13     13   6510 use Business::DK::CVR qw(_calculate_sum);
  13         209806  
  13         772  
7 13     13   6406 use Date::Calc qw(check_date);
  13         95476  
  13         1065  
8 13     13   102 use base 'Exporter';
  13         30  
  13         1207  
9 13     13   7042 use integer;
  13         213  
  13         82  
10 13     13   6877 use Tie::IxHash;
  13         33051  
  13         486  
11 13     13   95 use Readonly;
  13         29  
  13         760  
12 13     13   91 use Params::Validate qw( validate_pos SCALAR ARRAYREF );
  13         32  
  13         648  
13 13     13   220 use 5.012; #5.12.0
  13         49  
14              
15             our $VERSION = '0.17';
16             our @EXPORT_OK = qw(
17             validate
18             validateCPR
19             generate
20             validate1968
21             generate1968
22             validate2007
23             generate2007
24             calculate
25             );
26              
27 13     13   92 use constant MODULUS_OPERAND_1968 => 11;
  13         27  
  13         833  
28 13     13   92 use constant MODULUS_OPERAND_2007 => 6;
  13         209  
  13         756  
29 13     13   86 use constant DATE_LENGTH => 6;
  13         22  
  13         677  
30 13     13   92 use constant CONTROL_CIFER_LENGTH => 4;
  13         97  
  13         728  
31 13     13   87 use constant VALID => 1;
  13         49  
  13         795  
32 13     13   81 use constant VALID_MALE => 1;
  13         35  
  13         699  
33 13     13   78 use constant VALID_FEMALE => 2;
  13         35  
  13         694  
34 13     13   81 use constant INVALID => 0;
  13         23  
  13         658  
35 13     13   79 use constant FEMALE => 'female';
  13         24  
  13         697  
36 13     13   88 use constant MALE => 'male';
  13         27  
  13         24816  
37              
38             Readonly my @controlcifers => qw(4 3 2 7 6 5 4 3 2 1);
39              
40             my %female_seeds;
41             tie %female_seeds, 'Tie::IxHash',
42             4 => { max => 9994, min => 10 },
43             2 => { max => 9998, min => 8 },
44             6 => { max => 9996, min => 12 };
45              
46             my %male_seeds;
47             tie %male_seeds, 'Tie::IxHash',
48             1 => { max => 9997, min => 7 },
49             3 => { max => 9999, min => 9 },
50             5 => { max => 9995, min => 11 };
51              
52             sub merge {
53 23     23 1 43 my ( $left_hashref, $right_hashref ) = @_;
54              
55 23         34 my %hash = %{$right_hashref};
  23         93  
56              
57 23         927 foreach ( keys %{$left_hashref} ) {
  23         61  
58 69         805 $hash{$_} = $left_hashref->{$_};
59             }
60              
61 23         245 return \%hash;
62             }
63              
64             sub calculate {
65 4     4 1 2949 my ($birthdate) = @_;
66              
67 4         80 validate_pos( @_,
68             { type => SCALAR, callbacks => { 'date' => \&_checkdate } } );
69              
70 2         6 my @cprs;
71 2         6 for ( 1 .. 999 ) {
72 1998         4612 my $n = sprintf '%03s', $_;
73              
74             #From DK::Business::CVR
75 1998         4517 my $sum = _calculate_sum( ( $birthdate . $n ), \@controlcifers );
76 1998         169538 my $mod = $sum % MODULUS_OPERAND_1968;
77              
78 1998         2893 my $checkciffer = ( MODULUS_OPERAND_1968 - $mod );
79              
80 1998 100       3647 if ( $checkciffer < 10 ) {
81 1636         4211 push @cprs, ( $birthdate . $n . $checkciffer );
82             }
83             }
84              
85 2 100       11 if (wantarray) {
86 1         185 return @cprs;
87             }
88             else {
89 1         56 return scalar @cprs;
90             }
91             }
92              
93             sub validateCPR {
94              
95             #We postpone parameter validation
96 1     1 1 3 return validate(shift);
97             }
98              
99             sub _length {
100 861     861   4748 my ( $number, $length ) = @_;
101              
102 861 100       1993 if ( length($number) != $length ) {
103 2         24 croak "argument: $number has to be $length digits long";
104             }
105 859         1319 return 1;
106             }
107              
108             sub validate {
109 14     14 1 749 my ($controlnumber) = @_;
110              
111             #We postpone parameter validation
112              
113 14         21 my $rv;
114 14 100       24 if ( $rv = validate1968($controlnumber) ) {
115 2         9 return $rv;
116             }
117             else {
118 7         14 return validate2007($controlnumber);
119             }
120             }
121              
122             sub validate2007 {
123 29     29 1 807 my ($controlnumber) = @_;
124 29         402 validate_pos( @_, { type => SCALAR, regex => qr/^\d+$/ } );
125              
126 27         342 _checkdate( substr $controlnumber, 0, DATE_LENGTH );
127 23         68 _assert_controlnumber($controlnumber);
128              
129 23         53 my $control = substr $controlnumber, DATE_LENGTH, CONTROL_CIFER_LENGTH;
130              
131 23         51 my $remainder = $control % MODULUS_OPERAND_2007;
132              
133             #Our own merge, could be Hash::Merge's merge
134 23         41 my %seeds = %{ merge( \%male_seeds, \%female_seeds ) };
  23         54  
135              
136 23 100 100     175 if ( my $series = $seeds{$remainder} ) {
    100 66        
137 13 100       71 if ( $control < $seeds{$remainder}->{min} ) {
    50          
138 11         57 return INVALID;
139             }
140             elsif ( $control > $seeds{$remainder}->{max} ) {
141 0         0 return INVALID;
142             }
143             }
144             elsif ( ( $control == 0 or $control == 6 ) && $remainder == 0 ) {
145 4         24 return INVALID;
146             }
147              
148 8 100       36 if ( $female_seeds{$remainder} ) {
    100          
    50          
149 1         12 return VALID_FEMALE;
150             }
151             elsif ( $male_seeds{$remainder} ) {
152 1         15 return VALID_MALE;
153             }
154             elsif ( $remainder == 0 ) {
155 6 50       95 if ( _is_equal($control) ) {
156 6         45 return VALID_FEMALE;
157             }
158             else {
159 0         0 return VALID_MALE;
160             }
161             }
162             else {
163 0         0 return INVALID;
164             }
165             }
166              
167             sub validate1968 {
168 846     846 1 239945 my ($controlnumber) = @_;
169              
170 846         12383 validate_pos( @_, { type => SCALAR, regex => qr/^\d+$/ } );
171              
172 842         11060 _checkdate( substr $controlnumber, 0, DATE_LENGTH );
173 835         2229 _assert_controlnumber($controlnumber);
174              
175 835         2343 my $sum = _calculate_sum( $controlnumber, \@controlcifers );
176              
177             #Note this might look like it is turned upside down but no rest from the
178             #modulus calculation indicated validity
179 835 100       79238 if ( $sum % MODULUS_OPERAND_1968 ) {
180 9         29 return INVALID;
181             }
182             else {
183 826 100       1435 if ( _is_equal($sum) ) {
184 411         1444 return VALID_MALE;
185             }
186             else {
187 415         1473 return VALID_FEMALE;
188             }
189             }
190             }
191              
192             sub _is_equal {
193 832     832   1436 my ($operand) = @_;
194              
195 832         8057 validate_pos( @_, { type => SCALAR, regex => qr/^\d+$/ } );
196              
197 832         8793 return ( not( $operand % 2 ) );
198             }
199              
200             sub _assert_controlnumber {
201 863     863   2152 my ($controlnumber) = @_;
202              
203 863         8026 validate_pos( @_, { type => SCALAR, regex => qr/^\d+$/ } );
204              
205 861         8536 _length( $controlnumber, scalar @controlcifers );
206              
207 859         1283 return VALID;
208             }
209              
210             sub _checkdate {
211              
212 879     879   2510 my $dateregex = qr{
213             \A #beginning of line
214             (\d{2}) #day of month, 2 digit representation, 01-31
215             (\d{2}) #month, 2 digit representation jan 01 - dec 12
216             (\d{2}) #year, 2 digit representation
217             \Z #end of line
218             }xsm;
219              
220             #According to the documentation validate_pos gets two paramters, hence the
221             #second optional argument specification
222 879         8296 validate_pos(
223             @_,
224             { type => SCALAR, regex => $dateregex },
225             { type => ARRAYREF, optional => 1 },
226             );
227              
228             #Params::Validate does not capture for us, so we re-do our regex
229 870         9700 $_[0] =~ m/$dateregex/;
230              
231 870 100       4605 if ( not check_date( $3, $2, $1 ) ) {
232 9         110 croak "argument: $_[0] has to be a valid date in the format: ddmmyy";
233             }
234              
235 861         1895 return VALID;
236             }
237              
238             sub generate {
239 2     2 1 1572 my ( $birthdate, $gender ) = @_;
240              
241 2         59 validate_pos(
242             @_,
243             { type => SCALAR, callbacks => { 'date' => \&_checkdate }, },
244             { type => SCALAR, optional => 1, default => q{} },
245             );
246              
247 0         0 my @genders;
248              
249 0 0       0 if ($gender) {
250 0         0 push @genders, $gender;
251             }
252             else {
253 0         0 @genders = qw(male female);
254             }
255              
256 0         0 my %cprs;
257 0         0 foreach my $g (@genders) {
258 0         0 my @cprs2007 = generate2007( $birthdate, $g );
259              
260 0         0 my $i = 1;
261 0         0 foreach my $cpr (@cprs2007) {
262 0         0 $cprs{$cpr}++;
263             }
264             }
265              
266 0 0       0 if (wantarray) {
267 0         0 return keys %cprs;
268             }
269             else {
270 0         0 return scalar keys %cprs;
271             }
272             }
273              
274             sub generate2007 {
275 2     2 1 1554 my ( $birthdate, $gender ) = @_;
276              
277             #TODO assert gender?
278 2         54 validate_pos(
279             @_,
280             { type => SCALAR, callbacks => { 'date' => \&_checkdate }, },
281             { type => SCALAR, optional => 1 },
282             );
283              
284 0         0 my @cprs;
285             my %seeds;
286              
287 0 0       0 if ( defined $gender ) {
288 0 0       0 if ( $gender eq MALE ) {
    0          
289 0         0 %seeds = %male_seeds;
290             }
291             elsif ( $gender eq FEMALE ) {
292 0         0 %seeds = %female_seeds;
293             }
294             else {
295 0         0 carp("Unknown gender: $gender, assuming no gender");
296 0         0 $gender = undef;
297             }
298             }
299              
300 0 0       0 if ( not $gender ) {
301 0         0 %seeds = %{ merge( \%female_seeds, \%male_seeds ) };
  0         0  
302             }
303              
304 0         0 foreach my $seed ( keys %seeds ) {
305 0         0 my $s = $seeds{$seed}->{min};
306 0         0 while ( $s < $seeds{$seed}->{max} ) {
307 0         0 $s += MODULUS_OPERAND_2007;
308 0         0 push @cprs, ( $birthdate . sprintf '%04d', $s );
309             }
310             }
311              
312 0 0       0 if (wantarray) {
313 0         0 return @cprs;
314             }
315             else {
316 0         0 return scalar @cprs;
317             }
318             }
319              
320             sub generate1968 {
321 2     2 1 1560 my ( $birthdate, $gender ) = @_;
322              
323             #TODO assert gender?
324 2         62 validate_pos(
325             @_,
326             { type => SCALAR, callbacks => { 'date' => \&_checkdate }, },
327             { type => SCALAR, optional => 1, default => q{} },
328             );
329              
330 0           my @cprs;
331             my @malecprs;
332 0           my @femalecprs;
333              
334 0           my $checksum = 0;
335              
336 0           while ( $checksum < 9999 ) {
337              
338 0           my $cpr = $birthdate . sprintf '%04d', $checksum;
339              
340 0 0         if ( my $rv = validate1968($cpr) ) {
341              
342 0 0 0       if ( defined $gender and $rv ) {
343 0 0         if ( $rv == VALID_MALE ) {
    0          
344 0           push @malecprs, $cpr;
345             }
346             elsif ( $rv == VALID_FEMALE ) {
347 0           push @femalecprs, $cpr;
348             }
349              
350             }
351             else {
352 0           push @cprs, $cpr;
353             }
354             }
355 0           $checksum++;
356             }
357              
358 0 0 0       if ( $gender and $gender eq FEMALE ) {
    0 0        
359 0           @cprs = @femalecprs;
360             }
361             elsif ( $gender and $gender eq MALE ) {
362 0           @cprs = @malecprs;
363             }
364              
365 0 0         if (wantarray) {
366 0           return @cprs;
367             }
368             else {
369 0           return scalar @cprs;
370             }
371             }
372              
373             1;
374              
375             __END__
376              
377             =pod
378              
379             =begin markdown
380              
381             [![CPAN version](https://badge.fury.io/pl/Business-DK-CPR.svg)](http://badge.fury.io/pl/Business-DK-CPR)
382             [![Build Status](https://travis-ci.org/jonasbn/bdkcpr.svg?branch=master)](https://travis-ci.org/jonasbn/bdkcpr)
383             [![Coverage Status](https://coveralls.io/repos/jonasbn/bdkcpr/badge.png)](https://coveralls.io/r/jonasbn/bdkcpr)
384              
385             =end markdown
386              
387             =head1 NAME
388              
389             Business::DK::CPR - Danish CPR (SSN) number generator/validator
390              
391             =head1 VERSION
392              
393             This documentation describes version 0.17
394              
395             =head1 SYNOPSIS
396              
397             use Business::DK::CPR qw(validate);
398              
399             my $rv;
400             eval { $rv = validate(1501721111); };
401              
402             if ($@) {
403             die "Code is not of the expected format - $@";
404             }
405              
406             if ($rv) {
407             print 'CPR is valid';
408             } else {
409             print 'CPR is not valid';
410             }
411              
412             use Business::DK::CPR qw(calculate);
413              
414             my @cprs = calculate(150172);
415              
416             my $number_of_valid_cprs = calculate(150172);
417              
418              
419             #Using with Params::Validate
420             #See also examples/
421              
422             use Params::Validate qw(:all);
423             use Business::DK::CPR qw(validateCPR);
424              
425             sub check_cpr {
426             validate( @_,
427             { cpr =>
428             { callbacks =>
429             { 'validate_cpr' => sub { validateCPR($_[0]); } } } } );
430              
431             print $_[1]." is a valid CPR\n";
432              
433             }
434              
435             =head1 DESCRIPTION
436              
437             CPR stands for Central Person Registration and is the social security number
438             used in Denmark.
439              
440             =head1 SUBROUTINES AND METHODS
441              
442             All methods are exported by explicit request. None are exported implicitly.
443              
444             =head2 validate
445              
446             This function checks a CPR number for validity. It takes a CPR number as
447             argument and returns:
448              
449             =over
450              
451             =item * 1 (true) for valid male CPR number
452              
453             =item * 2 (true) for a valid female CPR number
454              
455             =item * 0 (false) for invalid CPR number
456              
457             =back
458              
459             It dies if the CPR number is malformed or in any way unparsable, be aware that
460             the 6 first digits are representing a date (SEE: L<_checkdate|/_checkdate> function below).
461              
462             In brief, the date indicate the person's birthday, the last 4 digits are
463             representing a serial number and control cipher.
464              
465             For a more thorough discussion on the format of CPR numbers please refer to the
466             L<SEE ALSO|/SEE ALSO> section.
467              
468             L<validate1968|/validate1968> is the old form of the CPR number. It is validated
469             using modulus 11.
470              
471             The new format introduced in 2001 (put to use in 2007, hence the name used
472             throughout this package) can be validated using L<validate2007|/validate2007> and
473             generate using L<generate2007|/generate2007>.
474              
475             The L<validate|/validate> subroutine wraps both validators and checks using against both.
476              
477             The L<generate|/generate> subroutine wraps both generators and accumulated the results.
478              
479             NB! it is possible to make fake CPR numbers that appear valid, please see
480             MOTIVATION and the L</calculate> function.
481              
482             L<validate|/validate> is also exported as: L<validateCPR|/validateCPR>, which is less imposing.
483              
484             =head2 validateCPR
485              
486             Better name for export. This is just a wrapper for L</validate>
487              
488             =head2 validate1968
489              
490             Validation against the original CPR algorithm introduced in 1968.
491              
492             =head2 validate2007
493              
494             Validation against the CPR algorithm introduced in 2007.
495              
496             =head2 generate
497              
498             This is a wrapper around calculate, so the naming is uniform to
499             L<Business::DK::CVR|Business::DK::CVR>
500              
501             This function takes an integer representing a date and calculates valid CPR
502             numbers for the specified date. In scalar context returns the number of valid
503             CPR numbers possible and in list context a list of valid CPR numbers.
504              
505             If the date is malformed or in any way invalid or unspecified the function dies.
506              
507             =head2 generate1968
508              
509             Specialized generator for L<validate1968|/validate1968> compatible CPR numbers. See: L<generate|/generate>
510              
511             =head2 generate2007
512              
513             Specialized generator for L<validate2007|/validate2007> compatible CPR numbers. See: L<generate|/generate>
514              
515             =head2 calculate
516              
517             See L<generate|/generate> and L<generate1968|/generate1968>. This is the old name for L<generate1968|/generate1968>.
518             It is just kept for backwards compatibility and it calls L<generate|/generate>.
519              
520             =head2 merge
521              
522             Mimics L<Hash::Merge|Hash::Merge>'s L<merge|Hash::Merge/merge> function. Takes two references to
523             hashes and returns a single reference to a hash containing the merge of the two
524             with the left parameter having precedence. The precedence has not meaning on
525             the case in this module, but then the behaviour is documented.
526              
527             =head1 PRIVATE FUNCTIONS
528              
529             =head2 _length
530              
531             This function validates the length of the argument, it dies if the argument
532             does not fit within the boundaries specified by the arguments provided:
533              
534             The B<_length> function takes the following arguments:
535              
536             =over
537              
538             =item number (mandatory), the number to be validated
539              
540             =item length required of number (mandatory)
541              
542             =back
543              
544             =head2 _assertdate
545              
546             This subroutine takes a digit integer representing a date in the format: DDMMYY.
547              
548             The date is checked for definedness, contents and length and finally, the
549             correctness of the date.
550              
551             The subroutine returns 1 indicating true upon successful assertion or
552             dies upon failure.
553              
554             =head2 _checkdate
555              
556             This subroutine takes a 6 digit integer representing a date in the format: DDMMYY.
557              
558             The subroutine returns 1 indicating true upon successful check or
559             dies upon failure.
560              
561             =head2 _assert_controlnumber
562              
563             This subroutine takes an 10 digit integer representing a complete CPR.
564             The CPR is tested for definedness, contents and length.
565              
566             The subroutine returns 1 indicating true upon successful assertion or
567             dies upon failure.
568              
569             =head1 EXPORTS
570              
571             Business::DK::CPR exports on request:
572              
573             =over
574              
575             =item * L<validate|/validate>
576              
577             =item * L<validateCPR|/validateCPR>
578              
579             =item * L<validate1968|/validate1968>
580              
581             =item * L<validate2007|/validate2007>
582              
583             =item * L<calculate|/calculate>
584              
585             =item * L<generate|/generate>
586              
587             =item * L<_checkdate|/_checkdate>
588              
589             =back
590              
591             =head1 DIAGNOSTICS
592              
593             =over
594              
595             =item * 'argument for birthdate should be provided', a data parameter has to be
596             provided.
597              
598             This error is thrown from L<_checkdate|/_checkdate>, which is used for all general parameter
599             validation.
600              
601             =item * 'argument: <birthdate> could not be parsed', the date provided is not
602             represented by 6 digits (see also below).
603              
604             This error is thrown from L<_checkdate|/_checkdate>, which is used for all general parameter
605             validation.
606              
607             =item * 'argument: <birthdate> has to be a valid date in the format: ddmmyy',
608             the date format used for CPR numbers has to adhere to ddmmyy in numeric format
609             like so: 311210, day in a two digit representation: 01-31, month also two digit
610             representation: 01-12 and finally year in a two digit representation: 00-99.
611              
612             This error is thrown from L<_checkdate|/_checkdate>, which is used for all general parameter
613             validation.
614              
615             =item * 'Unknown gender: <gender>, assuming no gender', this is just a warning
616             issued if a call to L<generate2007|/generate2007> has not been provided with a gender
617             parameter
618              
619             =back
620              
621             =head1 DEPENDENCIES
622              
623             =over
624              
625             =item * L<Business::DK::CVR|Business::DK::CVR>
626              
627             =item * L<Exporter|Exporter>
628              
629             =item * L<Carp|Carp>
630              
631             =item * L<Test::Exception|Test::Exception>
632              
633             =item * L<Date::Calc|Date::Calc>
634              
635             =item * L<Tie::IxHash|Tie::IxHash>
636              
637             =back
638              
639             =head1 CONFIGURATION AND ENVIRONMENT
640              
641             This module requires no special configuration or environment.
642              
643             =head1 INCOMPATIBILITIES
644              
645             There are no known incompatibilies in this package.
646              
647             =head1 TODO
648              
649             =over
650              
651             =item * Nothing to do, please refer to the distribution TODO file for the general
652             wish list and ideas for future expansions and experiments.
653              
654             =back
655              
656             =head1 TEST AND QUALITY
657              
658             The distribution uses the TEST_AUTHOR environment variable to run some
659             additional tests, which are interesting to the the author, these can be disabled
660             by not defining or setting the environment variable to something not positive.
661              
662             The distribution uses the TEST_CRITIC environment variable to control
663             L<Perl::Critic|Perl::Critic> tests.
664              
665             =head2 STANDARD TESTS
666              
667             Here are listed the standard tests, recommended for all CPAN-like distributions.
668             The matrix lists what flags are required to run the specific test.
669              
670             NONE TEST_AUTHOR TEST_CRITIC TEST_POD
671             --------------- ---- ----------- ----------- --------
672             00.load.t *
673             changes.t *
674             critic.t *
675             kwalitee.t *
676             pod-coverage.t *
677             pod.t *
678             prerequisites.t *
679             --------------- ---- ----------- ----------- --------
680              
681             All of the above tests are actually boilerplates and are maintained as separate
682             components.
683              
684             =head2 TEST COVERAGE
685              
686             Coverage of the test suite is at 89.1% for release 0.04, the coverage report
687             was generated with the TEST_AUTHOR flag enabled (SEE: L<TEST AND QUALITY|/TEST AND QUALITY>)
688              
689             ---------------------------- ------ ------ ------ ------ ------ ------ ------
690             File stmt bran cond sub pod time total
691             ---------------------------- ------ ------ ------ ------ ------ ------ ------
692             blib/lib/Business/DK/CPR.pm 74.2 41.9 53.8 100.0 100.0 72.9 70.3
693             .../Class/Business/DK/CPR.pm 89.1 85.7 77.8 71.4 100.0 27.1 86.0
694             Total 77.6 50.0 63.6 91.3 100.0 100.0 74.1
695             ---------------------------- ------ ------ ------ ------ ------ ------ ------
696              
697             =head2 PERL::CRITIC
698              
699             This section describes use of L<Perl::Critic|Perl::Critic> from a perspective of documenting
700             additions and exceptions to the standard use.
701              
702             =over
703              
704             =item * L<Perl::Critic::Policy::Miscellanea::ProhibitTies|Perl::Critic::Policy::Miscellanea::ProhibitTies>
705              
706             This package utilizes L<Tie::IxHash|Tie::IxHash> (SEE: L<DEPENDENCIES|/DEPENDENCIES>), this module
707             relies on tie.
708              
709             =item * L<Perl::Critic::Policy::NamingConventions::NamingConventions::Capitalization|Perl::Critic::Policy::NamingConventions::NamingConventions::Capitalization>
710              
711             CPR is an abbreviation for 'Centrale Person Register' (Central Person Register)
712             and it is kept in uppercase.
713              
714             =item * L<Perl::Critic::Policy::NamingConventions::ProhibitMixedCaseSubs|Perl::Critic::Policy::NamingConventions::ProhibitMixedCaseSubs> deprecated by the policy above.
715              
716             =item * L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma|Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma>
717              
718             Constants are good in most cases, see also:
719             L<http://logicLAB.jira.com/wiki/display/OPEN/Perl-Critic-Policy-ValuesAndExpressions-ProhibitConstantPragma>
720              
721             =item * L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers|Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers>
722              
723             Some values and boundaries are defined for certain intervals of numbers, these
724             are currently kept as is. Perhaps with a refactoring of the use of constants to
725             use of L<Readonly|Readonly> will address this.
726              
727             =back
728              
729             =head1 BUGS AND LIMITATIONS
730              
731             No known bugs at this time.
732              
733             Business::DK::CPR has some obvious flaws. The package can only check for
734             validity and format, whether a given CPR has been generated by some random
735             computer program and just resemble a CPR or whether a CPR has ever been assigned
736             to a person is not possible without access to central CPR database an access,
737             which is costly, limited and monitored.
738              
739             There are no other known limitations apart from the obvious flaws in the CPR
740             system (See: L<SEE ALSO|/SEE ALSO>).
741              
742             =head1 BUG REPORTING
743              
744             Please report issue via GitHub
745              
746             =over
747              
748             =item * L<GitHub Issues|https://github.com/jonasbn/perl-business-dk-cpr/issues>
749              
750             =back
751              
752             Alternatively report issues via CPAN RT:
753              
754             =over
755              
756             =item * L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-DK-CPR>
757              
758             =back
759              
760             or by sending mail to
761              
762             =over
763              
764             =item * C<bug-Business-DK-CPR@rt.cpan.org>
765              
766             =back
767              
768             =head1 SEE ALSO
769              
770             =over
771              
772             =item * L<http://www.cpr.dk/>
773              
774             =item * L<Class::Business::DK::CPR|Class::Business::DK::CPR>
775              
776             =item * L<Data::FormValidator::Constraints::Business::DK::CPR|Data::FormValidator::Constraints::Business::DK::CPR>
777              
778             =item * L<Business::DK::PO|Business::DK::PO>
779              
780             =item * L<Business::DK::CVR|Business::DK::CVR>
781              
782             =item * L<http://logicLAB.jira.com/wiki/display/OPEN/Perl-Critic-Policy-ValuesAndExpressions-ProhibitConstantPragma>
783              
784             =back
785              
786             =head1 MOTIVATION
787              
788             I write business related applications. So I need to be able to validate CPR
789             numbers once is a while, hence the validation function.
790              
791             The calculate/generate1968 function is however a different story. When I was in
792             school we where programming in Comal80 and some of the guys in my school created
793             lists of CPR numbers valid with their own birthdays. The thing was that if you
794             got caught riding the train without a valid ticket the personnel would only
795             check the validity of you CPR number, so all you have to remember was your
796             birthday and 4 more digits not being the actual last 4 digits of your CPR
797             number.
798              
799             I guess this was the first hack I ever heard about and saw - I never tried it
800             out, but back then it really fascinated me and my interest in computers was
801             really sparked.
802              
803             =head1 AUTHOR
804              
805             =over
806              
807             =item * Jonas B., (jonasbn) - C<< <jonasbn@cpan.org> >>
808              
809             =back
810              
811             =head1 ACKNOWLEDGEMENTS
812              
813             =over
814              
815             =item * Karen Etheridge (ETHER)
816              
817             =item * Neil Bowers (NEILB)
818              
819             =item * Mohammad S Anwar (MANWAR)
820              
821             =back
822              
823             =head1 COPYRIGHT
824              
825             Business-DK-CPR and related is (C) by Jonas B., (jonasbn) 2006-2020
826              
827             =head1 LICENSE
828              
829             Business-DK-CPR is released under the Artistic License 2.0
830              
831             =cut