1 <& elements/grid-report.html,
9 unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
11 my ($agentnum,$sel_agent);
12 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
14 $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } );
15 die "agentnum $agentnum not found!" unless $sel_agent;
17 my $title = $sel_agent ? $sel_agent->agent.' ' : '';
19 my ($refnum,$sel_part_referral);
20 if ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
22 $sel_part_referral = qsearchs('part_referral', { 'refnum' => $refnum } );
23 die "refnum $refnum not found!" unless $sel_part_referral;
25 $title .= $sel_part_referral->referral.' '
26 if $sel_part_referral;
28 $title .= 'Customer Accounting Summary Report';
30 my @items = ('netsales', 'cashflow');
31 my @params = ( [], [] );
32 my $grossdiscount = $cgi->param('grossdiscount');
33 my $setuprecur = $cgi->param('setuprecur');
34 if ($setuprecur && $grossdiscount) {
35 #see blocks below for more details on each option
36 @items = ('gross', 'discounted', 'receipts', 'gross', 'discounted', 'receipts');
38 [ setuprecur => 'setup' ],
39 [ setuprecur => 'setup' ],
40 [ setuprecur => 'setup' ],
41 [ setuprecur => 'recur' ],
42 [ setuprecur => 'recur' ],
43 [ setuprecur => 'recur' ],
45 } elsif ($setuprecur) {
46 # instead of 'cashflow' (payments - refunds), use 'receipts'
47 # (applied payments), because it's divisible into setup and recur.
48 @items = ('netsales', 'receipts', 'netsales', 'receipts');
50 [ setuprecur => 'setup' ],
51 [ setuprecur => 'setup' ],
52 [ setuprecur => 'recur' ],
53 [ setuprecur => 'recur' ],
55 } elsif ($grossdiscount) {
56 # instead of 'netsales' (invoiced - netcredits)
57 # use 'gross' (invoiced + discounted) and 'discounted' (sum of discounts on invoices)
58 @items = ('gross', 'discounted', 'cashflow');
59 @params = ( [], [], [] );
64 my @cross_params = ();
66 my $status = $cgi->param('status');
67 die "invalid status" unless $status =~ /^\w+|$/;
70 foreach (qw(agentnum refnum status)) {
71 if ( defined $cgi->param($_) ) {
72 $search_hash{$_} = $cgi->param($_);
75 $search_hash{'classnum'} = [ $cgi->param('cust_classnum') ]
76 if grep { $_ eq 'cust_classnum' } $cgi->param;
78 my $query = FS::cust_main::Search->search(\%search_hash);
79 my @cust_main = qsearch($query);
81 foreach my $cust_main (@cust_main) {
82 push @cross_params, [ ('custnum' => $cust_main->custnum) ];
88 cross_params => \@cross_params,
89 agentnum => $agentnum,
92 for ( qw(start_month start_year end_month end_year) ) {
93 if ( $cgi->param($_) =~ /^(\d+)$/ ) {
98 warn Dumper(OPTIONS => \%opt) if $cgi->param('debug');
99 my $report = FS::Report::Table::Monthly->new(%opt);
100 my $data = $report->data;
101 warn Dumper(DATA => $data) if $cgi->param('debug') >= 2;
105 my @rows; # hashes of row info
106 my @cells; # arrayrefs of cell info
107 # We use Excel currency format, but not Excel dates, because
108 # these are whole months and there's no nice way to express that.
109 # This is the historical behavior for monthly reports.
114 { header => 1, rowspan => 2, colspan => ($setuprecur ? 4 : 3) },
116 { header => 1, colspan => ($grossdiscount ? 3 : 2), value => time2str('%b %Y', $_) },
117 } @{ $data->{speriod} }
119 my $ncols = scalar(@{ $data->{speriod} });
126 { header => 1, value => mt('Gross') },
127 { header => 1, value => mt('Discount') }
129 : { header => 1, value => mt('Billed') }
131 { header => 1, value => mt('Paid') },
135 # use PDL; # ha ha, I just might.
137 foreach my $cust_main (@cust_main) { # correspond to cross_params
138 my $skip = 1; # skip the customer iff ALL of their values are zero
139 for my $subrow (0..($setuprecur ? 1 : 0)) { # the setup/recur axis
140 push @rows, { class => $subrow ? 'shaded' : '' };
142 if ( $subrow == 0 ) {
145 { value => $cust_main->name,
147 rowspan => ($setuprecur ? 2 : 1),
149 { value => $cust_main->state, #cust_main->bill_location->state,
151 rowspan => ($setuprecur ? 2 : 1),
153 { value => $cust_main->salesnum ? $cust_main->sales->salesperson : '',
155 rowspan => ($setuprecur ? 2 : 1),
162 { value => $subrow ? mt('recurring') : mt('setup'),
165 for my $col (0..$ncols-1) { # the month
166 for my $subcol (0..($grossdiscount ? 2 : 1)) { # the billed/paid or gross/discount/paid axis
167 my $item = $subrow * ($grossdiscount ? 3 : 2) + $subcol;
168 my $value = $data->{data}[$item][$col][$row];
169 $skip = 0 if abs($value) > 0.005;
170 push @thisrow, { value => sprintf('%0.2f', $value), format => 'money' };
171 $total[( ($ncols * $subrow) + $col ) * ($grossdiscount ? 3 : 2) + $subcol] += $value;
174 push @cells, \@thisrow;
177 # all values are zero--remove the rows we just added
187 for my $subrow (0..($setuprecur ? 1 : 0)) {
188 push @rows, { class => ($subrow ? 'totalshaded' : 'total') };
190 if ( $subrow == 0 ) {
192 { value => mt('Total'),
195 rowspan => ($setuprecur ? 2 : 1), };
199 { value => $subrow ? mt('recurring') : mt('setup'),
202 for my $col (0..($ncols * ($grossdiscount ? 3 : 2))-1) { # month and billed/paid or gross/discount/paid axis
203 my $value = $total[($subrow * $ncols * ($grossdiscount ? 3 : 2)) + $col];
204 push @thisrow, { value => sprintf('%0.2f', $value), format => 'money' };
206 push @cells, \@thisrow;
209 if ( $cgi->param('debug') >= 3 ) {
210 warn Dumper(\@rows, \@cells);
213 my $title = 'Customer Accounting Summary';