+ } elsif ( $format eq 'ics' ) {
+
+ my $bill = $cust_main->bill_location;
+ my $zip = $bill->zip;
+ my $zip4 = '';
+
+ $zip =~ s/\D//;
+ if ( $zip =~ /^(\d{5})(\d{4})$/ ) {
+ $zip = $1;
+ $zip4 = $2;
+ }
+
+ # minor false laziness with print_generic
+ my ($previous_balance) = $self->previous;
+ my $balance_due = $self->owed + $previous_balance;
+ my $payment_total = sum(0, map { $_->{'amount'} } $self->_items_payments);
+ my $credit_total = sum(0, map { $_->{'amount'} } $self->_items_credits);
+
+ my $past_due = '';
+ if ( $self->due_date and $time >= $self->due_date ) {
+ $past_due = sprintf('Past due:$%0.2f Due Immediately', $balance_due);
+ }
+
+ # again, bypass CSV
+ my $header = sprintf(
+ '%-10s%-30s%-48s%-2s%-50s%-30s%-30s%-25s%-2s%-5s%-4s%-8s%-8s%-10s%-10s%-10s%-10s%-10s%-10s%-480s%-35s',
+ $cust_main->display_custnum, #BID
+ uc($cust_main->first), #FNAME
+ uc($cust_main->last), #LNAME
+ '00', #BATCH, should this ever be anything else?
+ uc($cust_main->company), #COMP
+ uc($bill->address1), #STREET1
+ uc($bill->address2), #STREET2
+ uc($bill->city), #CITY
+ uc($bill->state), #STATE
+ $zip,
+ $zip4,
+ time2str('%Y%m%d', $self->_date), #BILL_DATE
+ $self->due_date2str('%Y%m%d'), #DUE_DATE,
+ ( map {sprintf('%0.2f', $_)}
+ $balance_due, #AMNT_DUE
+ $previous_balance, #PREV_BAL
+ $payment_total, #PYMT_RCVD
+ $credit_total, #CREDITS
+ $previous_balance, #BEG_BAL--is this correct?
+ $self->charged, #NEW_CHRG
+ ),
+ 'img01', #MRKT_MSG?
+ $past_due, #PAST_MSG
+ );
+
+ my @details;
+ my %svc_class = ('' => ''); # maybe cache this more persistently?
+
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+
+ my $show_pkgnum = $cust_bill_pkg->pkgnum || '';
+ my $cust_pkg = $cust_bill_pkg->cust_pkg if $show_pkgnum;
+
+ if ( $cust_pkg ) {
+
+ my @dates = ( $self->_date, undef );
+ if ( my $prev = $cust_bill_pkg->previous_cust_bill_pkg ) {
+ $dates[1] = $prev->sdate; #questionable
+ }
+
+ # generate an 01 detail for each service
+ my @svcs = $cust_pkg->h_cust_svc(@dates, 'I');
+ foreach my $cust_svc ( @svcs ) {
+ $show_pkgnum = ''; # hide it if we're showing svcnums
+
+ my $svcpart = $cust_svc->svcpart;
+ if (!exists($svc_class{$svcpart})) {
+ my $classnum = $cust_svc->part_svc->classnum;
+ my $part_svc_class = FS::part_svc_class->by_key($classnum)
+ if $classnum;
+ $svc_class{$svcpart} = $part_svc_class ?
+ $part_svc_class->classname :
+ '';
+ }
+
+ my @h_label = $cust_svc->label(@dates, 'I');
+ push @details, sprintf('01%-9s%-20s%-47s',
+ $cust_svc->svcnum,
+ $svc_class{$svcpart},
+ $h_label[1],
+ );
+ } #foreach $cust_svc
+ } #if $cust_pkg
+
+ my $desc = $cust_bill_pkg->desc; # itemdesc or part_pkg.pkg
+ if ($cust_bill_pkg->recur > 0) {
+ $desc .= ' '.time2str('%d-%b-%Y', $cust_bill_pkg->sdate).' to '.
+ time2str('%d-%b-%Y', $cust_bill_pkg->edate - 86400);
+ }
+ push @details, sprintf('02%-6s%-60s%-10s',
+ $show_pkgnum,
+ $desc,
+ sprintf('%0.2f', $cust_bill_pkg->setup + $cust_bill_pkg->recur),
+ );
+ } #foreach $cust_bill_pkg
+
+ # Tag this row so that we know whether this is one page (1), two pages
+ # (2), # or "big" (B). The tag will be stripped off before uploading.
+ if ( scalar(@details) < 12 ) {
+ push @details, '1';
+ } elsif ( scalar(@details) < 58 ) {
+ push @details, '2';
+ } else {
+ push @details, 'B';
+ }
+
+ return join('', $header, @details, "\n");
+
+ } else { # default