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 |