File Coverage

blib/lib/Business/RO/CNP.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Business::RO::CNP;
2              
3 1     1   31686 use Moose;
  0            
  0            
4             use DateTime::Format::Strptime;
5             use utf8;
6             use Carp;
7              
8             use 5.008_008;
9              
10             our $VERSION = '0.03';
11              
12             around BUILDARGS => sub {
13             my ( $orig, $class ) = ( shift, shift );
14              
15             if ( @_ == 1 && !ref $_[0] ) {
16             return $class->$orig( cnp => $_[0] );
17             }
18             else {
19             return $class->$orig(@_);
20             }
21             };
22              
23             has cnp => ( is => 'ro', isa => 'Int', required => 1, );
24              
25             has sex_id => (
26             is => 'ro',
27             isa => 'Int',
28             init_arg => undef,
29             default => sub { substr shift->cnp, 0, 1 },
30             );
31              
32             has sex => ( is => 'ro', isa => 'Str', init_arg => undef, lazy_build => 1, );
33              
34             sub _build_sex {
35             my $self = shift;
36             my %sexes = (
37             '1' => 'm',
38             '2' => 'f',
39             '3' => 'm',
40             '4' => 'f',
41             '5' => 'm',
42             '6' => 'f',
43             '7' => 'm',
44             '8' => 'f',
45             );
46             return
47             $self->sex_id == 9 ? 'unknown'
48             : !$self->sex_id ? undef
49             : $sexes{ $self->sex_id };
50             }
51              
52             has century =>
53             ( is => 'ro', isa => 'Int', init_arg => undef, lazy_build => 1, );
54              
55             sub _build_century {
56             my $self = shift;
57             my $sid = $self->sex_id;
58             return
59             $sid == 1
60             || $sid == 2 ? 19 : $sid == 3
61             || $sid == 4 ? 18 : $sid == 5
62             || $sid == 6 ? 20 : 19;
63             }
64              
65             has birthday =>
66             ( is => 'ro', isa => 'DateTime', init_arg => undef, lazy_build => 1, );
67              
68             sub _build_birthday {
69             my $self = shift;
70              
71             my $date = eval {
72             DateTime::Format::Strptime::strptime( '%Y%m%d',
73             $self->century . substr( $self->cnp, 1, 6 ) );
74             };
75              
76             if ($@) {
77             carp "Wrong date";
78             return q{};
79             }
80             else {
81             return $date;
82             }
83             }
84              
85             has county_id => (
86             is => 'ro',
87             isa => 'Int',
88             init_arg => undef,
89             default => sub { substr( shift->cnp, 7, 2 ) },
90             );
91              
92             has county => ( is => 'ro', isa => 'Str', init_arg => undef, lazy_build => 1, );
93              
94             sub _build_county {
95             my $self = shift;
96              
97             my %counties = (
98             '01' => 'Alba',
99             '02' => 'Arad',
100             '03' => 'ArgeÅŸ',
101             '04' => 'Bacău',
102             '05' => 'Bihor',
103             '06' => 'BistriÅ£a-Năsăud',
104             '07' => 'BotoÅŸani',
105             '08' => 'BraÅŸov',
106             '09' => 'Brăila',
107             '10' => 'Buzău',
108             '11' => 'CaraÅŸ-Severin',
109             '12' => 'Cluj',
110             '13' => 'ConstanÅ£a',
111             '14' => 'Covasna',
112             '15' => 'DâmboviÅ£a',
113             '16' => 'Dolj',
114             '17' => 'GalaÅ£i',
115             '18' => 'Gorj',
116             '19' => 'Harghita',
117             '20' => 'Hunedoara',
118             '21' => 'IalomiÅ£a',
119             '22' => 'IaÅŸi',
120             '23' => 'Ilfov',
121             '24' => 'MaramureÅŸ',
122             '25' => 'MehedinÅ£i',
123             '26' => 'MureÅŸ',
124             '27' => 'NeamÅ£',
125             '28' => 'Olt',
126             '29' => 'Prahova',
127             '30' => 'Satu Mare',
128             '31' => 'Sălaj',
129             '32' => 'Sibiu',
130             '33' => 'Suceava',
131             '34' => 'Teleorman',
132             '35' => 'TimiÅŸ',
133             '36' => 'Tulcea',
134             '37' => 'Vaslui',
135             '38' => 'Vâlcea',
136             '39' => 'Vrancea',
137             '40' => 'BucureÅŸti',
138             '41' => 'Sectorul 1',
139             '42' => 'Sectorul 2',
140             '43' => 'Sectorul 3',
141             '44' => 'Sectorul 4',
142             '45' => 'Sectorul 5',
143             '46' => 'Sectorul 6',
144             '51' => 'CălăraÅŸi',
145             '52' => 'Giurgiu',
146             );
147              
148             return $counties{ $self->county_id };
149             }
150              
151             has order_number => (
152             is => 'ro',
153             isa => 'Int',
154             init_arg => undef,
155             default => sub { substr( shift->cnp, 9, 3 ) },
156             );
157              
158             has checksum => (
159             is => 'ro',
160             isa => 'Int',
161             init_arg => undef,
162             default => sub { substr( shift->cnp, 12, 1 ) },
163             );
164              
165             has validator =>
166             ( is => 'ro', isa => 'Int', init_arg => undef, lazy_build => 1, );
167              
168             sub _build_validator {
169             my $self = shift;
170              
171             my @cnp = split //xms, substr( $self->cnp, 0, 12 );
172             my @check = split //xsm, '279146358279';
173              
174             my $sum;
175             for my $i ( 0 .. 11 ) {
176             $sum += $cnp[$i] * $check[$i];
177             }
178              
179             my $result = $sum % 11;
180             return $result == 10 ? 1 : $result;
181             }
182              
183             sub valid {
184             my ($self) = @_;
185             return $self->birthday && $self->checksum == $self->validator ? 1 : 0;
186             }
187              
188             1;
189              
190             __END__
191              
192             =head1 NAME
193              
194             Business::RO::CNP - Romanian CNP validation
195              
196             =head1 VERSION
197              
198             Version 0.02
199              
200             =head1 SYNOPSIS
201              
202             use Business::RO::CNP;
203              
204             my $cnp = Business::RO::CNP->new(cnp => 1040229319996);
205             #or:
206             my $cnp = Business::RO::CNP->new(1040229319996);
207              
208             print $cnp->valid ? "The CNP is valid" : "The CNP is not valid";
209              
210             =head1 DESCRIPTION
211              
212             This module checks the validation of CNP (personal numeric code) of Romania's citizens and offers information about the person.
213              
214             =head1 SUBROUTINES/METHODS
215              
216             =head2 valid
217              
218             This method returns 1 if the CNP is valid or 0 otherwise.
219              
220             It returns 1 when the L</birthday> method returns a valid birth date and when the last digit of the CNP returned by the method L</checksum> is equal to the value returned by the method L</validator>.
221              
222             =head2 sex
223              
224             This method returns 'm' if the person is a male or 'f' if is a female.
225              
226             The method returns 'unknown' if the sex id of the person (the first digit in the CNP) is 9 (for non-romanian citizens).
227              
228             When the first digit of the CNP is 1, 3, 5 or 7, this method returns 'm' and when it is 2, 4, 6 or 8, this method returns 'f'.
229              
230             print $cnp->sex;
231              
232             =head2 sex_id
233              
234             The method returns the first digit of the CNP. This digit is odd for men and even for women. It is 1 or 2 for those born between January 1 1900 and December 31 1999, 3 or 4 for those born between January 1 1800 and December 31 1899, 5 or 6 for those born between January 1 2000 and December 31 2099 and 7 or 8 for foreign citizens resident in Romania.
235              
236             The sex id 9 is also reserved for foreign citizens.
237              
238             print $cnp->sex_id;
239              
240             =head2 birthday
241              
242             This method returns a L<DateTime|DateTime> object that holds the birth day of the person so you can call any DateTime methods on it.
243              
244             print $cnp->birthday;
245             print $cnp->birthday->ymd;
246             print $cnp->birthday->strftime('%d %m %y');
247             print $cnp->birthday->set_locale('ro')->month_name;
248              
249             Check the L<DateTime|DateTime> module for finding out what methods you can use with this object.
250              
251             =head2 county
252              
253             This method returns the county where the person was born or where he received the CNP.
254              
255             print $cnp->county;
256              
257             =head2 county_id
258              
259             This method returns the county ID which is the pair of digits 8 and 9 in the CNP.
260              
261             Bucharest has the ID 40 but its sectors also have their own IDs.
262              
263             print $cnp->county_id;
264              
265             =head2 checksum
266              
267             This method returns the last digit in the CNP and represents a pre-calculated value based on the first 12 digits of the CNP.
268              
269             print $cnp->checksum;
270              
271             =head2 validator
272              
273             This method calculates the checksum from the first 12 digits of the CNP and it should be equal to the result of the checksum method in order to prove that the CNP is valid.
274              
275             print $cnp->validator;
276              
277             =head2 cnp
278              
279             This method returns the CNP given as parameter to the object constructor.
280              
281             print $cnp->cnp;
282              
283             =head2 order_number
284              
285             This method returns the 3-digit order number from the CNP.
286              
287             print $cnp->order_number;
288              
289             =head2 century
290              
291             This method returns the century in which the person was born.
292              
293             print $cnp->century;
294              
295             =head1 AUTHOR
296              
297             Octavian Rasnita, C<< <orasnita at gmail.com> >>
298              
299             =head1 INCOMPATIBILITIES
300              
301             No known incompatibilities.
302              
303             =head1 BUGS AND LIMITATIONS
304              
305             Please report any bugs or feature requests to C<bug-business-ro-cnp at rt.cpan.org>, or through
306             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Business-RO-CNP>. I will be notified, and then you'll
307             automatically be notified of progress on your bug as I make changes.
308              
309             =head1 DIAGNOSTICS
310              
311             =head1 CONFIGURATION AND ENVIRONMENT
312              
313             No configuration is necessary.
314              
315             =head1 DEPENDENCIES
316              
317             L<Moose|Moose>, L<DateTime::Format::Strptime|DateTime::Format::Strptime>
318              
319             =head1 SUPPORT
320              
321             You can find documentation for this module with the perldoc command.
322              
323             perldoc Business::RO::CNP
324              
325             You can also look for information at:
326              
327             =over 4
328              
329             =item * RT: CPAN's request tracker
330              
331             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-RO-CNP>
332              
333             =item * AnnoCPAN: Annotated CPAN documentation
334              
335             L<http://annocpan.org/dist/Business-RO-CNP>
336              
337             =item * CPAN Ratings
338              
339             L<http://cpanratings.perl.org/d/Business-RO-CNP>
340              
341             =item * Search CPAN
342              
343             L<http://search.cpan.org/dist/Business-RO-CNP/>
344              
345             =back
346              
347             =head1 ACKNOWLEDGEMENTS
348              
349             I found the algorithm for CNP validation on L<http://www.validari.ro/cnp> and the counties IDs on L<http://ro.wikipedia.org/wiki/Cod_numeric_personal>.
350              
351             =head1 LICENSE AND COPYRIGHT
352              
353             Copyright 2010 Octavian Rasnita.
354              
355             This program is free software; you can redistribute it and/or modify it
356             under the terms of either: the GNU General Public License as published
357             by the Free Software Foundation; or the Artistic License.
358              
359             See http://dev.perl.org/licenses/ for more information.
360              
361             =cut