package quantity + 477 report, #31282, from #24047
[freeside.git] / FS / FS / Report / FCC_477.pm
1 package FS::Report::FCC_477;
2 use base qw( FS::Report );
3
4 use strict;
5 use vars qw( @upload @download @technology @part2aoption @part2boption
6              %states
7              $DEBUG
8            );
9 use FS::Record qw( dbh );
10
11 use Tie::IxHash;
12 use Storable;
13
14 $DEBUG = 0;
15
16 =head1 NAME
17
18 FS::Report::FCC_477 - Routines for FCC Form 477 reports
19
20 =head1 SYNOPSIS
21
22 =head1 BUGS
23
24 Documentation.
25
26 =head1 SEE ALSO
27
28 =cut
29
30 @upload = qw(
31  <200kbps
32  200-768kbps
33  768kbps-1.5mbps
34  1.5-3mpbs
35  3-6mbps
36  6-10mbps
37  10-25mbps
38  25-100mbps
39  >100mbps
40 );
41
42 @download = qw(
43  200-768kbps
44  768kbps-1.5mbps
45  1.5-3mbps
46  3-6mbps
47  6-10mbps
48  10-25mbps
49  25-100mbps
50  >100mbps
51 );
52
53 @technology = (
54   'Asymmetric xDSL',
55   'Symmetric xDSL',
56   'Other Wireline',
57   'Cable Modem',
58   'Optical Carrier',
59   'Satellite',
60   'Terrestrial Fixed Wireless',
61   'Terrestrial Mobile Wireless',
62   'Electric Power Line',
63   'Other Technology',
64 );
65
66 @part2aoption = (
67  'LD carrier',
68  'owned loops',
69  'unswitched UNE loops',
70  'UNE-P',
71  'UNE-P replacement',
72  'FTTP',
73  'coax',
74  'wireless',
75 );
76
77 @part2boption = (
78  'nomadic',
79  'copper',
80  'FTTP',
81  'coax',
82  'wireless',
83  'other broadband',
84 );
85
86 #from the select at http://www.ffiec.gov/census/default.aspx
87 #though this is now in the database, also
88 %states = (
89   '01' => 'ALABAMA (AL)',
90   '02' => 'ALASKA (AK)',
91   '04' => 'ARIZONA (AZ)',
92   '05' => 'ARKANSAS (AR)',
93   '06' => 'CALIFORNIA (CA)',
94   '08' => 'COLORADO (CO)',
95
96   '09' => 'CONNECTICUT (CT)',
97   '10' => 'DELAWARE (DE)',
98   '11' => 'DISTRICT OF COLUMBIA (DC)',
99   '12' => 'FLORIDA (FL)',
100   '13' => 'GEORGIA (GA)',
101   '15' => 'HAWAII (HI)',
102
103   '16' => 'IDAHO (ID)',
104   '17' => 'ILLINOIS (IL)',
105   '18' => 'INDIANA (IN)',
106   '19' => 'IOWA (IA)',
107   '20' => 'KANSAS (KS)',
108   '21' => 'KENTUCKY (KY)',
109
110   '22' => 'LOUISIANA (LA)',
111   '23' => 'MAINE (ME)',
112   '24' => 'MARYLAND (MD)',
113   '25' => 'MASSACHUSETTS (MA)',
114   '26' => 'MICHIGAN (MI)',
115   '27' => 'MINNESOTA (MN)',
116
117   '28' => 'MISSISSIPPI (MS)',
118   '29' => 'MISSOURI (MO)',
119   '30' => 'MONTANA (MT)',
120   '31' => 'NEBRASKA (NE)',
121   '32' => 'NEVADA (NV)',
122   '33' => 'NEW HAMPSHIRE (NH)',
123
124   '34' => 'NEW JERSEY (NJ)',
125   '35' => 'NEW MEXICO (NM)',
126   '36' => 'NEW YORK (NY)',
127   '37' => 'NORTH CAROLINA (NC)',
128   '38' => 'NORTH DAKOTA (ND)',
129   '39' => 'OHIO (OH)',
130
131   '40' => 'OKLAHOMA (OK)',
132   '41' => 'OREGON (OR)',
133   '42' => 'PENNSYLVANIA (PA)',
134   '44' => 'RHODE ISLAND (RI)',
135   '45' => 'SOUTH CAROLINA (SC)',
136   '46' => 'SOUTH DAKOTA (SD)',
137
138   '47' => 'TENNESSEE (TN)',
139   '48' => 'TEXAS (TX)',
140   '49' => 'UTAH (UT)',
141   '50' => 'VERMONT (VT)',
142   '51' => 'VIRGINIA (VA)',
143   '53' => 'WASHINGTON (WA)',
144
145   '54' => 'WEST VIRGINIA (WV)',
146   '55' => 'WISCONSIN (WI)',
147   '56' => 'WYOMING (WY)',
148   '72' => 'PUERTO RICO (PR)',
149 );
150
151 sub restore_fcc477map {
152   my $key = shift;
153   FS::Record::scalar_sql('',"select formvalue from fcc477map where formkey = ?",$key);
154 }
155
156 sub save_fcc477map {
157   my $key = shift;
158   my $value = shift;
159
160   local $SIG{HUP} = 'IGNORE';
161   local $SIG{INT} = 'IGNORE';
162   local $SIG{QUIT} = 'IGNORE';
163   local $SIG{TERM} = 'IGNORE';
164   local $SIG{TSTP} = 'IGNORE';
165   local $SIG{PIPE} = 'IGNORE';
166
167   my $oldAutoCommit = $FS::UID::AutoCommit;
168   local $FS::UID::AutoCommit = 0;
169   my $dbh = dbh;
170
171   my $sql = "delete from fcc477map where formkey = ?";
172   my $sth = dbh->prepare($sql) or die dbh->errstr;
173   $sth->execute($key) or do {
174     warn "WARNING: Error removing FCC 477 form defaults: " . $sth->errstr;
175     $dbh->rollback if $oldAutoCommit;
176   };
177
178   $sql = "insert into fcc477map (formkey,formvalue) values (?,?)";
179   $sth = dbh->prepare($sql) or die dbh->errstr;
180   $sth->execute($key,$value) or do {
181     warn "WARNING: Error setting FCC 477 form defaults: " . $sth->errstr;
182     $dbh->rollback if $oldAutoCommit;
183   };
184
185   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
186
187   '';
188 }
189
190 sub parse_technology_option {
191   my $cgi = shift;
192   my $save = shift;
193   my @result = ();
194   my $i = 0;
195   for (my $i = 0; $i < scalar(@technology); $i++) {
196     my $value = $cgi->param("part1_technology_option_$i"); #lame
197     save_fcc477map("part1_technology_option_$i",$value) 
198         if $save && $value =~ /^\d+$/;
199     push @result, $value =~ /^\d+$/ ? $value : 0;
200   }
201   return (@result);
202 }
203
204 sub statenum2state {
205   my $num = shift;
206   $states{$num};
207 }
208 ### everything above this point is unmaintained ###
209
210
211 =head1 THE "NEW" REPORT (October 2014 and later)
212
213 =head2 METHODS
214
215 =over 4
216
217 =cut
218
219 # functions for internal use
220
221 sub join_optionnames {
222   join(' ', map { join_optionname($_) } @_);
223 }
224
225 sub join_optionnames_int {
226   join(' ', map { join_optionname_int($_) } @_);
227 }
228
229 sub join_optionname {
230   # Returns a FROM phrase to join a specific option into the query (via 
231   # part_pkg).  The option value will appear as a field with the same name
232   # as the option.
233   my $name = shift;
234   "LEFT JOIN (SELECT pkgpart, optionvalue AS $name FROM part_pkg_fcc_option".
235     " WHERE fccoptionname = '$name') AS t_$name".
236     " ON (part_pkg.pkgpart = t_$name.pkgpart)";
237 }
238
239 sub join_optionname_int {
240   # Returns a FROM phrase to join a specific option into the query (via 
241   # part_pkg) and cast it to integer..  Note this does not convert nulls
242   # to zero.
243   my $name = shift;
244   "LEFT JOIN (SELECT pkgpart, CAST(optionvalue AS int) AS $name
245    FROM part_pkg_fcc_option".
246     " WHERE fccoptionname = '$name') AS t_$name".
247     " ON (part_pkg.pkgpart = t_$name.pkgpart)";
248 }
249
250 sub dbaname {
251   # Returns an sql expression for the DBA name
252   "COALESCE( deploy_zone.dbaname,
253      (SELECT value FROM conf WHERE conf.name = 'company_name'
254                              AND (conf.agentnum = deploy_zone.agentnum
255                                   OR conf.agentnum IS NULL)
256                              ORDER BY conf.agentnum IS NOT NULL DESC
257                              LIMIT 1)
258      ) AS dbaname"
259 }
260
261 sub active_on {
262   # Returns a condition to limit packages to those that were setup before a 
263   # certain date, and not canceled before that date.
264   #
265   # (Strictly speaking this should also exclude suspended packages but 
266   # "suspended as of some past date" is a complicated query.)
267   my $date = shift;
268   "cust_pkg.setup <= $date AND ".
269   "(cust_pkg.cancel IS NULL OR cust_pkg.cancel > $date)";
270 }
271
272 sub is_fixed_broadband {
273   "is_broadband::int = 1 AND technology::int IN( 10, 11, 12, 20, 30, 40, 41, 42, 50, 60, 70, 90, 0 )"
274 }
275
276 sub is_mobile_broadband {
277   "is_broadband::int = 1 AND technology::int IN( 80, 81, 82, 83, 84, 85, 86, 87, 88)"
278 }
279
280 =item report SECTION, OPTIONS
281
282 Returns the report section SECTION (see the C<parts> method for section 
283 name strings) as an arrayref of arrayrefs.  OPTIONS may contain the following:
284
285 - date: a timestamp value. Packages that were active on that date will be 
286 counted.
287
288 - agentnum: limit to packages with this agent.
289
290 - detail: if true, the report will contain an additional column which contains
291 the keys of all objects aggregated in the row.
292
293 - ignore_quantity: if true, package quantities will be ignored (only distinct
294 packages will be counted).
295
296 =cut
297
298 sub report {
299   my $class = shift;
300   my $section = shift;
301   my %opt = @_;
302
303   my $method = $section.'_sql';
304   die "Report section '$section' is not implemented\n"
305     unless $class->can($method);
306   my $statement = $class->$method(%opt);
307
308   my $sth = dbh->prepare($statement);
309   $sth->execute or die $sth->errstr;
310   $sth->fetchall_arrayref;
311 }
312
313 sub fbd_sql {
314   my $class = shift;
315   my %opt = @_;
316   my $date = $opt{date} || time;
317   my $agentnum = $opt{agentnum};
318
319   my @select = (
320     'censusblock',
321     dbaname(),
322     'technology',
323     'CASE WHEN is_consumer IS NOT NULL THEN 1 ELSE 0 END',
324     'adv_speed_down',
325     'adv_speed_up',
326     'CASE WHEN is_business IS NOT NULL THEN 1 ELSE 0 END',
327     'cir_speed_down',
328     'cir_speed_up',
329   );
330   push @select, 'blocknum' if $opt{detail};
331
332   my $from = 'deploy_zone_block
333     JOIN deploy_zone USING (zonenum)
334     JOIN agent USING (agentnum)';
335   my @where = (
336     "zonetype = 'B'",
337     "active_date  < $date",
338     "(expire_date > $date OR expire_date IS NULL)",
339   );
340   push @where, "agentnum = $agentnum" if $agentnum;
341
342   my $order_by = 'censusblock, agentnum, technology, is_consumer, is_business';
343
344   "SELECT ".join(', ', @select) . "
345   FROM $from
346   WHERE ".join(' AND ', @where)."
347   ORDER BY $order_by
348   ";
349 }
350
351 sub fbs_sql {
352   my $class = shift;
353   my %opt = @_;
354   my $date = $opt{date} || time;
355   my $agentnum = $opt{agentnum};
356   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
357
358   my @select = (
359     'cust_location.censustract',
360     'technology',
361     'broadband_downstream',
362     'broadband_upstream',
363     "SUM($q)",
364     "SUM(COALESCE(is_consumer,0) * $q)",
365   );
366   push @select, "array_to_string(array_agg(pkgnum), ',')" if $opt{detail};
367
368   my $from =
369     'cust_pkg
370       JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
371       JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
372       JOIN part_pkg USING (pkgpart) '.
373       join_optionnames_int(qw(
374         is_broadband technology 
375         is_consumer
376         )).
377       join_optionnames(qw(broadband_downstream broadband_upstream))
378   ;
379   my @where = (
380     active_on($date),
381     is_fixed_broadband()
382   );
383   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
384   my $group_by = 'cust_location.censustract, technology, '.
385                    'broadband_downstream, broadband_upstream ';
386   my $order_by = $group_by;
387
388   "SELECT ".join(', ', @select) . "
389   FROM $from
390   WHERE ".join(' AND ', @where)."
391   GROUP BY $group_by
392   ORDER BY $order_by
393   ";
394
395 }
396
397 sub fvs_sql {
398   my $class = shift;
399   my %opt = @_;
400   my $date = $opt{date} || time;
401   my $agentnum = $opt{agentnum};
402   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
403
404   my @select = (
405     'cust_location.censustract',
406     # VoIP indicator (0 for non-VoIP, 1 for VoIP)
407     'COALESCE(is_voip, 0)',
408     # number of lines/subscriptions
409     "SUM($q * (CASE WHEN is_voip = 1 THEN 1 ELSE phone_lines END))",
410     # consumer grade lines/subscriptions
411     "SUM($q * COALESCE(is_consumer,0) * (CASE WHEN is_voip = 1 THEN voip_sessions ELSE phone_lines END))",
412   );
413   push @select, "array_to_string(array_agg(pkgnum), ',')" if $opt{detail};
414
415   my $from = 'cust_pkg
416     JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
417     JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
418     JOIN part_pkg USING (pkgpart) '.
419     join_optionnames_int(qw(
420       is_phone is_voip is_consumer phone_lines voip_sessions
421       ))
422   ;
423
424   my @where = (
425     active_on($date),
426     "(is_voip = 1 OR is_phone = 1)",
427   );
428   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
429   my $group_by = 'cust_location.censustract, COALESCE(is_voip, 0)';
430   my $order_by = $group_by;
431
432   "SELECT ".join(', ', @select) . "
433   FROM $from
434   WHERE ".join(' AND ', @where)."
435   GROUP BY $group_by
436   ORDER BY $order_by
437   ";
438
439 }
440
441 sub lts_sql {
442   my $class = shift;
443   my %opt = @_;
444   my $date = $opt{date} || time;
445   my $agentnum = $opt{agentnum};
446   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
447
448   my @select = (
449     "state.fips",
450     "SUM($q * phone_vges)",
451     "SUM($q * phone_circuits)",
452     "SUM($q * phone_lines)",
453     "SUM($q * (CASE WHEN is_broadband = 1 THEN phone_lines ELSE 0 END))",
454     "SUM($q * (CASE WHEN is_consumer = 1 AND phone_longdistance IS NULL THEN phone_lines ELSE 0 END))",
455     "SUM($q * (CASE WHEN is_consumer = 1 AND phone_longdistance = 1 THEN phone_lines ELSE 0 END))",
456     "SUM($q * (CASE WHEN is_consumer IS NULL AND phone_longdistance IS NULL THEN phone_lines ELSE 0 END))",
457     "SUM($q * (CASE WHEN is_consumer IS NULL AND phone_longdistance = 1 THEN phone_lines ELSE 0 END))",
458     "SUM($q * (CASE WHEN phone_localloop = 'owned' THEN phone_lines ELSE 0 END))",
459     "SUM($q * (CASE WHEN phone_localloop = 'leased' THEN phone_lines ELSE 0 END))",
460     "SUM($q * (CASE WHEN phone_localloop = 'resale' THEN phone_lines ELSE 0 END))",
461     "SUM($q * (CASE WHEN media = 'Fiber' THEN phone_lines ELSE 0 END))",
462     "SUM($q * (CASE WHEN media = 'Cable Modem' THEN phone_lines ELSE 0 END))",
463     "SUM($q * (CASE WHEN media = 'Fixed Wireless' THEN phone_lines ELSE 0 END))",
464   );
465   push @select, "array_to_string(array_agg(pkgnum),',')" if $opt{detail};
466
467   my $from =
468     'cust_pkg
469       JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
470       JOIN state USING (country, state)
471       JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
472       JOIN part_pkg USING (pkgpart) '.
473       join_optionnames_int(qw(
474         is_phone is_broadband
475         phone_vges phone_circuits phone_lines
476         is_consumer phone_longdistance
477         )).
478       join_optionnames('media', 'phone_localloop')
479   ;
480   my @where = (
481     active_on($date),
482     "is_phone = 1",
483   );
484   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
485   my $group_by = 'state.fips';
486   my $order_by = $group_by;
487
488   "SELECT ".join(', ', @select) . "
489   FROM $from
490   WHERE ".join(' AND ', @where)."
491   GROUP BY $group_by
492   ORDER BY $order_by
493   ";
494 }
495
496 sub voip_sql {
497   my $class = shift;
498   my %opt = @_;
499   my $date = $opt{date} || time;
500   my $agentnum = $opt{agentnum};
501   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
502
503   my @select = (
504     "state.fips",
505     # OTT, OTT + consumer
506     "SUM($q * (CASE WHEN (voip_lastmile IS NULL) THEN 1 ELSE 0 END))",
507     "SUM($q * (CASE WHEN (voip_lastmile IS NULL AND is_consumer = 1) THEN 1 ELSE 0 END))",
508     # non-OTT: total, consumer, broadband bundle, media types
509     "SUM($q * (CASE WHEN (voip_lastmile = 1) THEN 1 ELSE 0 END))",
510     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND is_consumer = 1) THEN 1 ELSE 0 END))",
511     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND is_broadband = 1) THEN 1 ELSE 0 END))",
512     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND media = 'Copper') THEN 1 ELSE 0 END))",
513     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND media = 'Cable Modem') THEN 1 ELSE 0 END))",
514     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND media = 'Fiber') THEN 1 ELSE 0 END))",
515     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND media = 'Fixed Wireless') THEN 1 ELSE 0 END))",
516     "SUM($q * (CASE WHEN (voip_lastmile = 1 AND media NOT IN('Copper', 'Fiber', 'Cable Modem', 'Fixed Wireless') ) THEN 1 ELSE 0 END))",
517   );
518   push @select, "array_to_string(array_agg(pkgnum),',')" if $opt{detail};
519
520   my $from =
521     'cust_pkg
522       JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
523       JOIN state USING (country, state)
524       JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
525       JOIN part_pkg USING (pkgpart) '.
526       join_optionnames_int(
527         qw( is_voip is_broadband is_consumer voip_lastmile)
528       ).
529       join_optionnames('media')
530   ;
531   my @where = (
532     active_on($date),
533     "is_voip = 1",
534   );
535   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
536   my $group_by = 'state.fips';
537   my $order_by = $group_by;
538
539   "SELECT ".join(', ', @select) . "
540   FROM $from
541   WHERE ".join(' AND ', @where)."
542   GROUP BY $group_by
543   ORDER BY $order_by
544   ";
545 }
546
547 sub mbs_sql {
548   my $class = shift;
549   my %opt = @_;
550   my $date = $opt{date} || time;
551   my $agentnum = $opt{agentnum};
552   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
553
554   my @select = (
555     'state.fips',
556     'broadband_downstream',
557     'broadband_upstream',
558     "SUM($q)",
559     "SUM(COALESCE(is_consumer, 0) * $q)",
560   );
561   push @select, "array_to_string(array_agg(pkgnum),',')" if $opt{detail};
562
563   my $from =
564     'cust_pkg
565       JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
566       JOIN state USING (country, state)
567       JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
568       JOIN part_pkg USING (pkgpart) '.
569       join_optionnames_int(qw(
570         is_broadband technology
571         is_consumer
572         )).
573       join_optionnames(qw(broadband_downstream broadband_upstream))
574   ;
575   my @where = (
576     active_on($date),
577     is_mobile_broadband()
578   );
579   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
580   my $group_by = 'state.fips, broadband_downstream, broadband_upstream ';
581   my $order_by = $group_by;
582
583   "SELECT ".join(', ', @select) . "
584   FROM $from
585   WHERE ".join(' AND ', @where)."
586   GROUP BY $group_by
587   ORDER BY $order_by
588   ";
589 }
590
591 sub mvs_sql {
592   my $class = shift;
593   my %opt = @_;
594   my $date = $opt{date} || time;
595   my $agentnum = $opt{agentnum};
596   my $q = $opt{ignore_quantity} ? '1' : 'COALESCE(cust_pkg.quantity, 1)';
597
598   my @select = (
599     'state.fips',
600     "SUM($q)",
601     "SUM($q * COALESCE(mobile_direct,0))",
602   );
603   push @select, "array_to_string(array_agg(pkgnum),',')" if $opt{detail};
604
605   my $from =
606     'cust_pkg
607       JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum)
608       JOIN state USING (country, state)
609       JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
610       JOIN part_pkg USING (pkgpart) '.
611       join_optionnames_int(qw( is_mobile mobile_direct) )
612   ;
613   my @where = (
614     active_on($date),
615     'is_mobile = 1'
616   );
617   push @where, "cust_main.agentnum = $agentnum" if $agentnum;
618   my $group_by = 'state.fips';
619   my $order_by = $group_by;
620
621   "SELECT ".join(', ', @select) . "
622   FROM $from
623   WHERE ".join(' AND ', @where)."
624   GROUP BY $group_by
625   ORDER BY $order_by
626   ";
627 }
628
629 =item parts
630
631 Returns a Tie::IxHash reference of the internal short names used for the 
632 report sections ('fbd', 'mbs', etc.) to the full names.
633
634 =cut
635
636 tie our %parts, 'Tie::IxHash', (
637   fbd   => 'Fixed Broadband Deployment',
638   fbs   => 'Fixed Broadband Subscription',
639   fvs   => 'Fixed Voice Subscription',
640   lts   => 'Local Exchange Telephone Subscription',
641   voip  => 'Interconnected VoIP Subscription',
642   mbd   => 'Mobile Broadband Deployment',
643   mbsa  => 'Mobile Broadband Service Availability',
644   mbs   => 'Mobile Broadband Subscription',
645   mvd   => 'Mobile Voice Deployment',
646   mvs   => 'Mobile Voice Subscription',
647 );
648
649 sub parts {
650   Storable::dclone(\%parts);
651 }
652
653 =item part_table SECTION
654
655 Returns the name of the primary table that's aggregated in the report section 
656 SECTION. The last column of the report returned by the L</report> method is 
657 a comma-separated list of record numbers, in this table, that are included in
658 the report line item.
659
660 =cut
661
662 sub part_table {
663   my ($class, $part) = @_;
664   if ($part eq 'fbd') {
665     return 'deploy_zone_block';
666   } else {
667     return 'cust_pkg';
668   } # add other cases as we add more of the deployment/availability reports
669 }
670
671 1;