File Coverage

blib/lib/Parse/SAMGov/Exclusion.pm
Criterion Covered Total %
statement 113 116 97.4
branch 32 48 66.6
condition n/a
subroutine 17 17 100.0
pod 0 3 0.0
total 162 184 88.0


line stmt bran cond sub pod time code
1             package Parse::SAMGov::Exclusion;
2             $Parse::SAMGov::Exclusion::VERSION = '0.202';
3 5     5   289129 use strict;
  5         11  
  5         209  
4 5     5   21 use warnings;
  5         8  
  5         281  
5 5     5   86 use 5.010;
  5         15  
6 5     5   22 use Carp;
  5         8  
  5         376  
7 5     5   763 use Data::Dumper;
  5         7579  
  5         264  
8 5     5   866 use DateTime;
  5         693087  
  5         145  
9 5     5   814 use DateTime::Format::Strptime;
  5         79920  
  5         93  
10 5     5   1738 use Parse::SAMGov::Mo;
  5         10  
  5         37  
11 5     5   2222 use Parse::SAMGov::Exclusion::Name;
  5         34  
  5         179  
12 5     5   616 use Parse::SAMGov::Entity::Address;
  5         12  
  5         1819  
13              
14             #ABSTRACT: defines the SAM Exclusions object
15              
16             use overload fallback => 1,
17             '""' => sub {
18 20     20   30258 my $self = $_[0];
19 20         54 my $str = '';
20 20         84 $str .= $self->classification . ': ' . $self->name;
21 20 50       67 $str .= "\nAddress: " . $self->address if $self->address;
22 20 100       87 $str .= "\nUEI: " . $self->UEI if $self->UEI;
23 20 50       57 $str .= "\nDUNS: " . $self->DUNS if $self->DUNS;
24 20 50       64 $str .= "\nCAGE: " . $self->CAGE if $self->CAGE;
25 20 50       57 $str .= "\nSAM no.: " . $self->SAM_number if $self->SAM_number;
26 20 50       54 $str .= "\nNational Provider Identifier: " . $self->NPI if $self->NPI;
27 20 50       58 $str .= "\nCreation Date: " . $self->creation_date->ymd('-') if $self->creation_date;
28 20 50       50 $str .= "\nActive Date: " . $self->active_date->ymd('-') if $self->active_date;
29 20 100       405 if ($self->termination_date->year() == 2200) {
30 4         26 $str .= "\nTermination Date: Indefinite";
31             } else {
32 16 50       105 $str .= "\nTermination Date: " . $self->termination_date->ymd('-') if $self->termination_date;
33             }
34 20 100       224 $str .= "\nCross Reference: " . $self->crossref if $self->crossref;
35 20 50       80 $str .= "\nRecord status: " . $self->record_status if $self->record_status;
36 20 50       52 $str .= "\nExclusion Program: " . $self->xprogram if $self->xprogram;
37 20 50       48 $str .= "\nExclusion Agency: " . $self->xagency if $self->xagency;
38 20 50       54 $str .= "\nExclusion Type: " . $self->xtype if $self->xtype;
39 20 100       51 $str .= "\nExclusion Type(Cause & Treatment Code): " . $self->CT_code if $self->CT_code;
40 20 100       51 $str .= "\nAdditional Comments: " . $self->comments if $self->comments;
41 20 100       54 $str .= "\nD&B Open Data Flag: " . $self->dnb_open_data if defined $self->dnb_open_data;
42 20         115 return $str;
43 5     5   58 };
  5         9  
  5         96  
44              
45              
46             has classification => ();
47              
48              
49             has name => default => sub {
50             return Parse::SAMGov::Exclusion::Name->new;
51             };
52              
53              
54             has address => default => sub {
55             return Parse::SAMGov::Entity::Address->new;
56             };
57              
58              
59             has 'DUNS';
60             has 'dnb_open_data';
61             has 'UEI';
62              
63              
64             has 'xprogram';
65              
66              
67             has 'xagency';
68              
69              
70             has 'CT_code';
71              
72              
73             has 'xtype';
74              
75              
76             has 'comments';
77              
78             sub _parse_date {
79 60 50   60   161 if (@_) {
80 60         98 my $d = shift;
81 60 100       264 $d = '12/31/2200' if $d =~ /indefinite/i;
82 60         153 state $Strp =
83             DateTime::Format::Strptime->new(pattern => '%m/%d/%Y',
84             time_zone => 'America/New_York',);
85 60         33276 return $Strp->parse_datetime($d);
86             }
87 0         0 return;
88             }
89              
90              
91             has creation_date => (coerce => sub { _parse_date $_[0] });
92              
93              
94             has active_date => (coerce => sub { _parse_date $_[0] });
95              
96              
97             has termination_date => (coerce => sub { _parse_date $_[0] });
98              
99              
100             has 'record_status';
101              
102              
103             has 'crossref';
104              
105              
106             has 'SAM_number';
107              
108              
109             has 'CAGE';
110              
111              
112             has 'NPI';
113              
114             sub _trim {
115             # from Mojo::Util::trim
116 476     476   886 my $s = shift;
117 476         916 $s =~ s/^\s+//g;
118 476         1027 $s =~ s/\s+$//g;
119 476         1591 return $s;
120             }
121              
122             sub load {
123 20     20 0 66 my $self = shift;
124 20         41 my $ncols = scalar(@_);
125 20 100       91 return $self->load_v2(@_) if $ncols eq 31;
126 4 50       23 return $self->load_v1(@_) if $ncols eq 28;
127 0         0 carp "Unknown version of data file found with $ncols columns";
128 0         0 return undef;
129             }
130              
131             sub load_v1 {
132 4     4 0 8 my $self = shift;
133 4 50       15 return unless scalar(@_) == 28;
134 4         11 $self->classification(_trim(shift));
135 4         13 my $name = Parse::SAMGov::Exclusion::Name->new(
136             entity => _trim(shift),
137             prefix => _trim(shift),
138             first => _trim(shift),
139             middle => _trim(shift),
140             last => _trim(shift),
141             suffix => _trim(shift),
142             );
143 4         20 $self->name($name);
144 4         23 my $addr = Parse::SAMGov::Entity::Address->new(
145             # the order of shifting matters
146             address => _trim(join(' ', shift, shift, shift, shift)),
147             city => _trim(shift),
148             state => _trim(shift),
149             country => _trim(shift),
150             zip => _trim(shift),
151             );
152 4         20 $self->address($addr);
153 4         11 $self->DUNS(_trim(shift));
154 4         12 $self->xprogram(_trim(shift));
155 4         11 $self->xagency(_trim(shift));
156 4         10 $self->CT_code(_trim(shift));
157 4         7 $self->xtype(_trim(shift));
158 4         8 $self->comments(_trim(shift));
159 4         12 $self->active_date(shift);
160 4         17 $self->termination_date(shift);
161 4         16 $self->record_status(_trim(shift));
162 4         25 $self->crossref(_trim(shift));
163 4         11 $self->SAM_number(_trim (shift));
164 4         11 $self->CAGE(_trim(shift));
165 4         10 $self->NPI(_trim(shift));
166 4         24 return 1;
167             }
168              
169             sub load_v2 {
170 16     16 0 25 my $self = shift;
171 16 50       40 return unless scalar(@_) == 31;
172 16         33 $self->classification(_trim(shift));
173 16         41 my $name = Parse::SAMGov::Exclusion::Name->new(
174             entity => _trim(shift),
175             prefix => _trim(shift),
176             first => _trim(shift),
177             middle => _trim(shift),
178             last => _trim(shift),
179             suffix => _trim(shift),
180             );
181 16         75 $self->name($name);
182 16         72 my $addr = Parse::SAMGov::Entity::Address->new(
183             # the order of shifting matters
184             address => _trim(join(' ', shift, shift, shift, shift)),
185             city => _trim(shift),
186             state => _trim(shift),
187             country => _trim(shift),
188             zip => _trim(shift),
189             );
190 16         64 $self->address($addr);
191 16         73 $self->dnb_open_data(shift);# D&B Open Data Flag
192 16         48 $self->DUNS(_trim(shift));
193 16         37 $self->UEI(_trim(shift));
194 16         34 $self->xprogram(_trim(shift));
195 16         37 $self->xagency(_trim(shift));
196 16         29 $self->CT_code(_trim(shift));
197 16         47 $self->xtype(_trim(shift));
198 16         32 $self->comments(_trim(shift));
199 16         53 $self->active_date(shift);
200 16         67 $self->termination_date(shift);
201 16         59 $self->record_status(_trim(shift));
202 16         38 $self->crossref(_trim(shift));
203 16         38 $self->SAM_number(_trim (shift));
204 16         39 $self->CAGE(_trim(shift));
205 16         34 $self->NPI(_trim(shift));
206 16         50 $self->creation_date(shift);
207 16         82 return 1;
208             }
209              
210             1;
211              
212             =pod
213              
214             =encoding UTF-8
215              
216             =head1 NAME
217              
218             Parse::SAMGov::Exclusion - defines the SAM Exclusions object
219              
220             =head1 VERSION
221              
222             version 0.202
223              
224             =head1 SYNOPSIS
225              
226             my $exclusion = Parse::SAMGov::Exclusion->new;
227             $exclusion->classification("firm");
228             $exclusion->DUNS('123456789');
229             $exclusion->CAGE('7ABZ1');
230              
231             ...
232              
233             =head1 METHODS
234              
235             =head2 new
236              
237             This method creates a new Exclusion object.
238              
239             =head2 classification
240              
241             Identifies the exclusion Classification Type as either a Firm, Individual,
242             Special Entity Designation, or Vessel. The maximum length of this field is 50
243             characters.
244              
245             =head2 name
246              
247             This sets/gets an object of L<Parse::SAMGov::Exclusion::Name> which can hold
248             either the entity name being excluded or the individual name being excluded.
249              
250             =head2 address
251              
252             This sets/gets an object of L<Parse::SAMGov::Entity::Address> which holds the
253             primary address of the entity or individual being excluded. It includes the
254             city, two character abbreviation of state/province, three character abbreviation
255             of country and a 10 character zip/postal code.
256              
257             =head2 DUNS
258              
259             This holds the unique identifier of the excluded entity, currently the Data
260             Universal Numbering System (DUNS) number. Exclusion records with a
261             classification type of Firm must have a DUNS number. It may be found in
262             exclusion records of other classification types if the individual, special
263             entity or vessel has a DUNS number. This has a maximum length of 9 characters.
264              
265             =head2 dnb_open_data
266              
267             This flag denotes whether this is a D&B Open Data or not. V2 only.
268              
269             =head2 UEI
270              
271             This holds the SAM Unique Entity Identifier (UEI) and is 12 characters long. This number is only valid for V2 files on or after 2022.
272              
273             =head2 xprogram
274              
275             Exclusion Program identifies if the exclusion is Reciprocal, Nonreciprocal or Procurement.
276             For any exclusion record created on or after August 25, 1995, the value will
277             always be Reciprocal.
278              
279             =head2 agency
280              
281             Exclusion Agency identifies the agency which created the exclusion.
282              
283             =head2 CT_code
284              
285             This identifies the legacy Excluded Parties List System (EPLS) Cause & Treatment
286             (CT) Code associated with the exclusion. CT Codes were replayed by the Exclusion
287             Type in SAM. Exclusions created after August 2012 will not have CT Codes. They
288             will only have Exclusion Types.
289              
290             =head2 xtype
291              
292             This identifies the Exclusion Type for the record replacing the CT Code.
293             Exclusion Type is a simplified, easier to understand way to identify why the
294             entity is being excluded.
295              
296             =head2 comments
297              
298             This field provides the agency creating the exclusion space to enter additional
299             information as necessary. The maximum length allowed is 4000 characters.
300              
301             =head2 creation_date
302              
303             This field identifies the date the exclusion was created in SAM. It returns a DateTime
304             object. It accepts an input of the format MM/DD/YYYY, and converts it to a
305             DateTime object with the timezone used as America/New_York.
306              
307             =head2 active_date
308              
309             This field identifies the date the exclusion went active. It returns a DateTime
310             object. It accepts an input of the format MM/DD/YYYY, and converts it to a
311             DateTime object with the timezone used as America/New_York.
312              
313             =head2 termination_date
314              
315             This field identifies the date the exclusion will be terminated. The date
316             '12/31/2200' is denoted as indefinite exclusion for now. This field also returns
317             a DateTime object.
318              
319             =head2 record_status
320              
321             This identifies the record as begin Active or Inactive. This can be blank if the
322             record is active.
323              
324             =head2 crossref
325              
326             Identifies other names/aliases with which the entity being excluded has been
327             identified. For example, companies who do business under other names may have
328             those other names listed here.
329              
330             =head2 SAM_number
331              
332             The internal number used by SAM to identify exclusion records. Since only Firm
333             exclusion records are required to have a DUNS number, SAM needed a way to
334             uniquely track exclusion records of other classification types.
335              
336             =head2 CAGE
337              
338             The CAGE code associated with the excluded entity. Mostly found on Firm
339             exclusion records, but could be in other types if the Individual, Special
340             Entity, or Vessel has a CAGE code.
341              
342             =head2 NPI
343              
344             The National Provider Identifier (NPI) associated with the exclusion. Healthcare
345             providers acquire their unique 10-digit NPIs from the Centers for Medicare &
346             Medicaid Services (CMS) at the Department of Health & Human Services (HHS) to
347             identify themselves in a standard way throughout their industry.
348              
349             =head1 AUTHOR
350              
351             Vikas N Kumar <vikas@cpan.org>
352              
353             =head1 COPYRIGHT AND LICENSE
354              
355             This software is copyright (c) 2023 by Selective Intellect LLC.
356              
357             This is free software; you can redistribute it and/or modify it under
358             the same terms as the Perl 5 programming language system itself.
359              
360             =cut
361              
362             __END__
363             ### COPYRIGHT: Selective Intellect LLC.
364             ### AUTHOR: Vikas N Kumar <vikas@cpan.org>