summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/AccessRight.pm3
-rw-r--r--FS/FS/Conf.pm18
-rw-r--r--FS/FS/Report/Table.pm21
-rw-r--r--FS/FS/Schema.pm20
-rw-r--r--FS/FS/Upgrade.pm1
-rw-r--r--FS/FS/access_right.pm38
-rw-r--r--FS/FS/cust_bill.pm34
-rw-r--r--FS/FS/cust_pay.pm27
-rw-r--r--FS/FS/part_event/Condition.pm2
-rw-r--r--FS/FS/part_event/Condition/has_cust_tag.pm49
-rw-r--r--FS/FS/part_pkg/discount_Mixin.pm4
-rwxr-xr-xFS/FS/svc_broadband.pm48
-rw-r--r--FS/FS/upgrade_journal.pm151
-rw-r--r--FS/MANIFEST2
-rw-r--r--FS/t/upgrade_journal.t5
-rwxr-xr-xhttemplate/edit/cust_pay.cgi26
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi5
-rw-r--r--httemplate/elements/city.html6
-rw-r--r--httemplate/elements/menu.html2
-rw-r--r--httemplate/elements/tr-select-cust_tag.html2
-rw-r--r--httemplate/graph/cust_signup.html29
-rw-r--r--httemplate/graph/report_cust_signup.html6
-rwxr-xr-xhttemplate/search/cust_main.cgi2
-rw-r--r--httemplate/search/elements/search-html.html6
-rw-r--r--httemplate/search/elements/search.html10
-rw-r--r--httemplate/view/cust_main/payment_history.html2
-rw-r--r--httemplate/view/cust_pay.html22
27 files changed, 501 insertions, 40 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 06263c27b..d2417f069 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -253,6 +253,7 @@ tie my %rights, 'Tie::IxHash',
###
'Reporting/listing rights' => [
'List customers',
+ 'List all customers',
'List zip codes', #NEW
'List invoices',
'List packages',
@@ -267,6 +268,8 @@ tie my %rights, 'Tie::IxHash',
{ rightname=> 'List inventory', global=>1 },
{ rightname=>'View email logs', global=>1 },
+ 'Download report data',
+
#{ rightname => 'List customers of all agents', global=>1 },
],
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 895346928..4a4f92c8a 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -1572,6 +1572,13 @@ and customer address. Include units.',
},
{
+ 'key' => 'disable_maxselect',
+ 'section' => 'UI',
+ 'description' => 'Prevent changing the number of records per page.',
+ 'type' => 'checkbox',
+ },
+
+ {
'key' => 'session-start',
'section' => 'session',
'description' => 'If defined, the command which is executed on the Freeside machine when a session begins. The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
@@ -2269,6 +2276,13 @@ and customer address. Include units.',
},
{
+ 'key' => 'require_cash_deposit_info',
+ 'section' => 'billing',
+ 'description' => 'When recording cash payments, display bank deposit information fields.',
+ 'type' => 'checkbox',
+ },
+
+ {
'key' => 'paymentforcedtobatch',
'section' => 'deprecated',
'description' => 'See batch-enable_payby and realtime-disable_payby. Used to (for CHEK): Cause per customer payment entry to be forced to a batch processor rather than performed realtime.',
@@ -2911,7 +2925,7 @@ and customer address. Include units.',
'section' => 'invoicing',
'description' => 'Enable FTP of raw invoice data - format.',
'type' => 'select',
- 'select_enum' => [ '', 'default', 'billco', ],
+ 'select_enum' => [ '', 'default', 'oneline', 'billco', ],
},
{
@@ -2947,7 +2961,7 @@ and customer address. Include units.',
'section' => 'invoicing',
'description' => 'Enable spooling of raw invoice data - format.',
'type' => 'select',
- 'select_enum' => [ '', 'default', 'billco', ],
+ 'select_enum' => [ '', 'default', 'oneline', 'billco', ],
},
{
diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm
index 3942543b5..b0e911f84 100644
--- a/FS/FS/Report/Table.pm
+++ b/FS/FS/Report/Table.pm
@@ -32,21 +32,32 @@ options in %opt.
=over 4
-=item signups: The number of customers signed up.
+=item signups: The number of customers signed up. Options are "refnum"
+(limit by advertising source) and "indirect" (boolean, tells us to limit
+to customers that have a referral_custnum that matches the advertising source).
=cut
sub signups {
my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_;
- my @where = (
- $self->in_time_period_and_agent($speriod, $eperiod, $agentnum, 'signupdate')
+ my @where = ( $self->in_time_period_and_agent($speriod, $eperiod, $agentnum,
+ 'cust_main.signupdate')
);
- if ( $opt{'refnum'} ) {
+ my $join = '';
+ if ( $opt{'indirect'} ) {
+ $join = " JOIN cust_main AS referring_cust_main".
+ " ON (cust_main.referral_custnum = referring_cust_main.custnum)";
+
+ if ( $opt{'refnum'} ) {
+ push @where, "referring_cust_main.refnum = ".$opt{'refnum'};
+ }
+ }
+ elsif ( $opt{'refnum'} ) {
push @where, "refnum = ".$opt{'refnum'};
}
$self->scalar_sql(
- "SELECT COUNT(*) FROM cust_main WHERE ".join(' AND ', @where)
+ "SELECT COUNT(*) FROM cust_main $join WHERE ".join(' AND ', @where)
);
}
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 961862da0..fdf29e0a0 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1332,12 +1332,17 @@ sub tables_hashref {
# index into payby table
# eventually
'payinfo', 'varchar', 'NULL', 512, '', '', #see cust_main above
- 'paymask', 'varchar', 'NULL', $char_d, '', '',
+ 'paymask', 'varchar', 'NULL', $char_d, '', '',
'paydate', 'varchar', 'NULL', 10, '', '',
'paybatch', 'varchar', 'NULL', $char_d, '', '', #for auditing purposes.
'payunique', 'varchar', 'NULL', $char_d, '', '', #separate paybatch "unique" functions from current usage
'closed', 'char', 'NULL', 1, '', '',
'pkgnum', 'int', 'NULL', '', '', '', #desired pkgnum for pkg-balances
+ # cash/check deposit info fields
+ 'bank', 'varchar', 'NULL', $char_d, '', '',
+ 'depositor', 'varchar', 'NULL', $char_d, '', '',
+ 'account', 'varchar', 'NULL', 20, '', '',
+ 'teller', 'varchar', 'NULL', 20, '', '',
],
'primary_key' => 'paynum',
#i guess not now, with cust_pay_pending, if we actually make it here, we _do_ want to record it# 'unique' => [ [ 'payunique' ] ],
@@ -3599,6 +3604,19 @@ sub tables_hashref {
'index' => [],
},
+ 'upgrade_journal' => {
+ 'columns' => [
+ 'upgradenum', 'serial', '', '', '', '',
+ '_date', 'int', '', '', '', '',
+ 'upgrade', 'varchar', '', $char_d, '', '',
+ 'status', 'varchar', '', $char_d, '', '',
+ 'statustext', 'varchar', 'NULL', $char_d, '', '',
+ ],
+ 'primary_key' => 'upgradenum',
+ 'unique' => [ [ 'upgradenum' ] ],
+ 'index' => [ [ 'upgrade' ] ],
+ },
+
%{ tables_hashref_torrus() },
# tables of ours for doing torrus virtual port combining
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index 8f66c66b5..aabc4e72f 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -7,6 +7,7 @@ use Tie::IxHash;
use FS::UID qw( dbh driver_name );
use FS::Conf;
use FS::Record qw(qsearchs qsearch str2time_sql);
+use FS::upgrade_journal;
use FS::svc_domain;
$FS::svc_domain::whois_hack = 1;
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index ef8cc6cd8..d2a39aac8 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -180,6 +180,44 @@ sub _upgrade_data { # class method
}
+ my @all_groups = qsearch('access_group', {});
+
+ ### ACL_list_all_customers
+ if ( !FS::upgrade_journal->is_done('ACL_list_all_customers') ) {
+
+ # grant "List all customers" to all users who have "List customers"
+ for my $group (@all_groups) {
+ if ( $group->access_right('List customers') ) {
+ my $access_right = FS::access_right->new( {
+ 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $group->groupnum,
+ 'rightname' => 'List all customers',
+ } );
+ my $error = $access_right->insert;
+ die $error if $error;
+ }
+ }
+
+ FS::upgrade_journal->set_done('ACL_list_all_customers');
+ }
+
+ ### ACL_download_report_data
+ if ( !FS::upgrade_journal->is_done('ACL_download_report_data') ) {
+
+ # grant to everyone
+ for my $group (@all_groups) {
+ my $access_right = FS::access_right->new( {
+ 'righttype' => 'FS::access_group',
+ 'rightobjnum' => $group->groupnum,
+ 'rightname' => 'Download report data',
+ } );
+ my $error = $access_right->insert;
+ die $error if $error;
+ }
+
+ FS::upgrade_journal->set_done('ACL_download_report_data');
+ }
+
'';
}
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 3aa75eca5..945771e0d 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -2005,6 +2005,36 @@ sub print_csv {
'0', # 29 | Other Taxes & Fees*** NUM* 9
);
+ } elsif ( lc($opt{'format'}) eq 'oneline' ) { #name?
+
+ my ($previous_balance) = $self->previous;
+ my $totaldue = sprintf('%.2f', $self->owed + $previous_balance);
+ my @items = map {
+ ($_->{pkgnum} || ''),
+ $_->{description},
+ $_->{amount}
+ } $self->_items_pkg;
+
+ $csv->combine(
+ $cust_main->agentnum,
+ $self->custnum,
+ $cust_main->first,
+ $cust_main->last,
+ $cust_main->address1,
+ $cust_main->address2,
+ $cust_main->city,
+ $cust_main->state,
+ $cust_main->zip,
+
+ # invoice fields
+ time2str("%x", $self->_date),
+ $self->invnum,
+ $self->charged,
+ $totaldue,
+
+ @items,
+ );
+
} else {
$csv->combine(
@@ -2044,6 +2074,10 @@ sub print_csv {
}
+ } elsif ( lc($opt{'format'}) eq 'oneline' ) {
+
+ #do nothing
+
} else {
foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index d98d11ecb..ef30809b0 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -113,6 +113,22 @@ books closed flag, empty or `Y'
Desired pkgnum when using experimental package balances.
+=item bank
+
+The bank where the payment was deposited.
+
+=item depositor
+
+The name of the depositor.
+
+=item account
+
+The deposit account number.
+
+=item teller
+
+The teller number.
+
=back
=head1 METHODS
@@ -493,8 +509,11 @@ sub check {
|| $self->ut_textn('payunique')
|| $self->ut_enum('closed', [ '', 'Y' ])
|| $self->ut_foreign_keyn('pkgnum', 'cust_pkg', 'pkgnum')
+ || $self->ut_textn('bank')
+ || $self->ut_alphan('depositor')
+ || $self->ut_numbern('account')
+ || $self->ut_numbern('teller')
|| $self->payinfo_check()
- || $self->ut_numbern('discount_term')
;
return $error if $error;
@@ -509,6 +528,12 @@ sub check {
return "invalid discount_term"
if ($self->discount_term && $self->discount_term < 2);
+ if ( $self->payby eq 'CASH' and $conf->exists('require_cash_deposit_info') ) {
+ foreach (qw(bank depositor account teller)) {
+ return "$_ required" if $self->get($_) eq '';
+ }
+ }
+
#i guess not now, with cust_pay_pending, if we actually make it here, we _do_ want to record it
# # UNIQUE index should catch this too, without race conditions, but this
# # should give a better error message the other 99.9% of the time...
diff --git a/FS/FS/part_event/Condition.pm b/FS/FS/part_event/Condition.pm
index ef3d0b561..c75990893 100644
--- a/FS/FS/part_event/Condition.pm
+++ b/FS/FS/part_event/Condition.pm
@@ -344,7 +344,7 @@ sub condition_sql_option_option {
}
-#used for part_event/Condition/cust_bill_has_service.pm
+#used for part_event/Condition/cust_bill_has_service.pm and has_cust_tag.pm
#a little false laziness w/above and condition_sql_option_integer
sub condition_sql_option_option_integer {
my( $class, $option, $driver_name ) = @_;
diff --git a/FS/FS/part_event/Condition/has_cust_tag.pm b/FS/FS/part_event/Condition/has_cust_tag.pm
new file mode 100644
index 000000000..cde933881
--- /dev/null
+++ b/FS/FS/part_event/Condition/has_cust_tag.pm
@@ -0,0 +1,49 @@
+package FS::part_event::Condition::has_cust_tag;
+
+use strict;
+
+use base qw( FS::part_event::Condition );
+use FS::Record qw( qsearch );
+
+sub description {
+ 'Customer has tag',
+}
+
+sub eventtable_hashref {
+ { 'cust_main' => 1,
+ 'cust_bill' => 1,
+ 'cust_pkg' => 1,
+ };
+}
+
+#something like this
+sub option_fields {
+ (
+ 'tagnum' => { 'label' => 'Customer tag',
+ 'type' => 'select-cust_tag',
+ 'multiple' => 1,
+ },
+ );
+}
+
+sub condition {
+ my( $self, $object ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my $hashref = $self->option('tagnum') || {};
+ grep $hashref->{ $_->tagnum }, $cust_main->cust_tag;
+}
+
+sub condition_sql {
+ my( $self, $table ) = @_;
+
+ my $matching_tags =
+ "SELECT tagnum FROM cust_tag WHERE cust_tag.custnum = $table.custnum".
+ " AND cust_tag.tagnum IN ".
+ $self->condition_sql_option_option_integer('tagnum');
+
+ "EXISTS($matching_tags)";
+}
+
+1;
diff --git a/FS/FS/part_pkg/discount_Mixin.pm b/FS/FS/part_pkg/discount_Mixin.pm
index 7de3bfe42..7c53a8c4c 100644
--- a/FS/FS/part_pkg/discount_Mixin.pm
+++ b/FS/FS/part_pkg/discount_Mixin.pm
@@ -84,7 +84,9 @@ sub calc_discount {
$amount += $discount->amount
if $cust_pkg->pkgpart == $param->{'real_pkgpart'};
$amount += sprintf('%.2f', $discount->percent * $br / 100 );
- my $chg_months = $param->{'months'} || $cust_pkg->part_pkg->freq;
+ my $chg_months = defined($param->{'months'}) ?
+ $param->{'months'} :
+ $cust_pkg->part_pkg->freq;
my $months = $discount->months
? min( $chg_months,
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
index 3a936a972..67b1ffd2b 100755
--- a/FS/FS/svc_broadband.pm
+++ b/FS/FS/svc_broadband.pm
@@ -463,7 +463,7 @@ sub assign_ip_addr {
my @blocks;
my $ip_addr;
- if ( $self->blocknum and $self->addr_block->routernum == $self->routernum ) {
+ if ( $self->addr_block and $self->addr_block->routernum == $self->routernum ) {
# simple case: user chose a block, find an address in that block
# (this overrides an existing IP address if it's not in the block)
@blocks = ($self->addr_block);
@@ -482,15 +482,13 @@ sub assign_ip_addr {
return '';
}
$ip_addr = $block->next_free_addr;
- last if $ip_addr;
- }
- if ( $ip_addr ) {
- $self->set(ip_addr => $ip_addr->addr);
- return '';
- }
- else {
- return 'No IP address available on this router';
+ if ( $ip_addr ) {
+ $self->set(ip_addr => $ip_addr->addr);
+ $self->set(blocknum => $block->blocknum);
+ return '';
+ }
}
+ return 'No IP address available on this router';
}
=item assign_router
@@ -667,6 +665,38 @@ sub _upgrade_data {
": no routernum in address block ".$addr_block->cidr.", skipped\n";
}
}
+
+ # assign blocknums to services that should have them
+ my @all_blocks = qsearch('addr_block', { });
+ SVC: foreach my $self (
+ qsearch({
+ 'select' => 'svc_broadband.*',
+ 'table' => 'svc_broadband',
+ 'addl_from' => 'JOIN router USING (routernum)',
+ 'hashref' => {},
+ 'extra_sql' => 'WHERE svc_broadband.blocknum IS NULL '.
+ 'AND router.manual_addr IS NULL',
+ })
+ ) {
+
+ next SVC if $self->ip_addr eq '';
+ my $NetAddr = $self->NetAddr;
+ # inefficient, but should only need to run once
+ foreach my $block (@all_blocks) {
+ if ($block->NetAddr->contains($NetAddr)) {
+ $self->set(blocknum => $block->blocknum);
+ my $error = $self->replace;
+ warn "WARNING: error assigning blocknum ".$block->blocknum.
+ " to service ".$self->svcnum."\n$error; skipped\n"
+ if $error;
+ next SVC;
+ }
+ }
+ warn "WARNING: no block found containing ".$NetAddr->addr." for service ".
+ $self->svcnum;
+ #next SVC;
+ }
+
'';
}
diff --git a/FS/FS/upgrade_journal.pm b/FS/FS/upgrade_journal.pm
new file mode 100644
index 000000000..8f6d121a3
--- /dev/null
+++ b/FS/FS/upgrade_journal.pm
@@ -0,0 +1,151 @@
+package FS::upgrade_journal;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::upgrade_journal - Object methods for upgrade_journal records
+
+=head1 SYNOPSIS
+
+ use FS::upgrade_journal;
+
+ $record = new FS::upgrade_journal \%hash;
+ $record = new FS::upgrade_journal { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ # Typical use case
+ my $upgrade = 'rename_all_customers_to_Bob';
+ if ( ! FS::upgrade_journal->is_done($upgrade) ) {
+ ... # do the upgrade, then, if it succeeds
+ FS::upgrade_journal->set_done($upgrade);
+ }
+
+=head1 DESCRIPTION
+
+An FS::upgrade_journal object records an upgrade procedure that was run
+on the database. FS::upgrade_journal inherits from FS::Record. The
+following fields are currently supported:
+
+=over 4
+
+=item upgradenum - primary key
+
+=item _date - unix timestamp when the upgrade was run
+
+=item upgrade - string identifier for the upgrade procedure; must match /^\w+$/
+
+=item status - either 'done' or 'failed'
+
+=item statustext - any other message that needs to be recorded
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new upgrade record. To add it to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'upgrade_journal'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+sub delete { die "upgrade_journal records can't be deleted" }
+sub replace { die "upgrade_journal records can't be modified" }
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ if ( !$self->_date ) {
+ $self->_date(time);
+ }
+
+ my $error =
+ $self->ut_numbern('upgradenum')
+ || $self->ut_number('_date')
+ || $self->ut_alpha('upgrade')
+ || $self->ut_text('status')
+ || $self->ut_textn('statustext')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item is_done UPGRADE
+
+Returns the upgrade entry with identifier UPGRADE and status 'done', if
+there is one. This is an easy way to check whether an upgrade has been done.
+
+=cut
+
+sub is_done {
+ my ($class, $upgrade) = @_;
+ qsearch('upgrade_journal', { 'status' => 'done', 'upgrade' => $upgrade })
+}
+
+=item set_done UPGRADE
+
+Creates and inserts an upgrade entry with the current time, status 'done',
+and identifier UPGRADE. Dies on error.
+
+=cut
+
+sub set_done {
+ my ($class, $upgrade) = @_;
+ my $new = $class->new({ 'status' => 'done', 'upgrade' => $upgrade });
+ my $error = $new->insert;
+ die $error if $error;
+ $new;
+}
+
+
+=head1 BUGS
+
+Despite how it looks, this is not currently suitable for use as a mutex.
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 49d27b437..55c45b936 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -630,3 +630,5 @@ FS/tower_sector.pm
t/tower_sector.t
FS/h_svc_cert.pm
t/h_svc_cert.t
+FS/upgrade_journal.pm
+t/upgrade_journal.t
diff --git a/FS/t/upgrade_journal.t b/FS/t/upgrade_journal.t
new file mode 100644
index 000000000..0822effc5
--- /dev/null
+++ b/FS/t/upgrade_journal.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::upgrade_journal;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
index 3fd9c79eb..7a1bb00fa 100755
--- a/httemplate/edit/cust_pay.cgi
+++ b/httemplate/edit/cust_pay.cgi
@@ -18,7 +18,7 @@
<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
<INPUT TYPE="hidden" NAME="paybatch" VALUE="<% $paybatch %>">
-<BR><BR>
+<BR>
<% mt('Payment') |h %>
<% ntable("#cccccc", 2) %>
@@ -56,7 +56,29 @@
<TD ALIGN="right"><% mt('Check #') |h %></TD>
<TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
</TR>
-% }
+% }
+% elsif ( $payby eq 'CASH' and $conf->exists('require_cash_deposit_info') ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Bank') |h %></TD>
+ <TD COLSPAN=3><INPUT TYPE="text" NAME="bank" VALUE="<% $cgi->param('bank') %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Check #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Teller #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="teller" VALUE="<% $cgi->param('teller') %>" SIZE=10></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Depositor') |h %></TD>
+ <TD COLSPAN=3><INPUT TYPE="text" NAME="depositor" VALUE="<% $cgi->param('depositor') %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Account #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="account" VALUE="<% $cgi->param('account') %>" SIZE=18></TD>
+ </TR>
+% }
<TR>
% if ( $link eq 'custnum' || $link eq 'popup' ) {
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
index e74f9022f..06f5e64d5 100755
--- a/httemplate/edit/process/cust_pay.cgi
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -28,6 +28,8 @@
%}
<%init>
+my $conf = FS::Conf->new;
+
$cgi->param('linknum') =~ /^(\d+)$/
or die "Illegal linknum: ". $cgi->param('linknum');
my $linknum = $1;
@@ -46,6 +48,7 @@ my $new = new FS::cust_pay ( {
$_, scalar($cgi->param($_));
} qw( paid payby payinfo paybatch
pkgnum discount_term
+ bank depositor account teller
)
#} fields('cust_pay')
} );
@@ -57,6 +60,6 @@ push @rights, 'Post cash payment' if $new->payby eq 'CASH';
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right(\@rights);
-my $error = $new->insert( 'manual' => 1 );
+my $error ||= $new->insert( 'manual' => 1 );
</%init>
diff --git a/httemplate/elements/city.html b/httemplate/elements/city.html
index f6d2b4bad..6a2142f29 100644
--- a/httemplate/elements/city.html
+++ b/httemplate/elements/city.html
@@ -107,7 +107,11 @@ function <% $pre %>county_changed(what, callback) {}
<% $text_style %>
>
-% if ( !$disable_select ) {
+% if ( $disable_select ) {
+%# avoid JS errors
+<INPUT TYPE="hidden" ID="city_select">
+% }
+% else {
<SELECT NAME = "<%$pre%>city_select"
ID = "<%$pre%>city_select"
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index d61d44e89..ce2bbe9c0 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -106,7 +106,7 @@ $report_customers_lists{'with USPS-unvalidated addresses'} = [ $fsurl. 'search/c
tie my %report_customers, 'Tie::IxHash';
$report_customers{'List customers'} = [ \%report_customers_lists, 'List customers' ]
- if $curuser->access_right('List customers');
+ if $curuser->access_right('List all customers');
$report_customers{'Zip code distribution'} = [ $fsurl. 'search/report_cust_main-zip.html', 'Zip codes by number of customers' ];
$report_customers{'Customer signup report'} = [ $fsurl. 'graph/report_cust_signup.html', 'New customer signups by date' ],
$report_customers{'Advanced customer reports'} = [ $fsurl. 'search/report_cust_main.html', 'by status, signup date, agent, etc.' ]
diff --git a/httemplate/elements/tr-select-cust_tag.html b/httemplate/elements/tr-select-cust_tag.html
index b2b6d967e..5312644ef 100644
--- a/httemplate/elements/tr-select-cust_tag.html
+++ b/httemplate/elements/tr-select-cust_tag.html
@@ -28,7 +28,7 @@ my $cgi = $opt{'cgi'};
my $is_report = $opt{'is_report'};
my @curr_tagnum = ();
-if ( $cgi->param('error') ) {
+if ( $cgi && $cgi->param('error') ) {
@curr_tagnum = $cgi->param('tagnum');
} elsif ( $opt{'custnum'} ) {
@curr_tagnum = map $_->tagnum,
diff --git a/httemplate/graph/cust_signup.html b/httemplate/graph/cust_signup.html
index dd9100f1e..a3eb702f2 100644
--- a/httemplate/graph/cust_signup.html
+++ b/httemplate/graph/cust_signup.html
@@ -9,7 +9,7 @@
'agentnum' => $agentnum,
'sprintf' => '%u',
'disable_money' => 1,
- 'bottom_total' => (scalar @items > 1 ? 1 : 0),
+ 'bottom_total' => (scalar @items > 1 && !$indirect ? 1 : 0),
'bottom_link' => $bottom_link,
'link_fromparam' => 'signupdate_begin',
'link_toparam' => 'signupdate_end',
@@ -59,25 +59,34 @@ elsif ( $cgi->param('refnum') =~ /^(\d*)$/ ) {
}
}
+my $indirect = ($cgi->param('indirect') eq 'Y' ? 1 : 0);
+
my (@items, @labels, @colors, @params, @links);
my $hue = 0;
-my $hue_increment = 125;
+my $hue_increment = 75;
my @signup_colors;
foreach my $referral (@referral) {
+ my %params = ('refnum' => $referral->refnum) unless $all_referral;
+
push @items, 'signups';
push @labels, ( $all_referral ? 'Signups' : $referral->referral );
- push @params, ( $all_referral ? [] : [ 'refnum' => $referral->refnum ] );
+ push @params, [ %params ];
push @links, $link . ($all_referral ? '' : "refnum=".$referral->refnum.';');
- if ( !@signup_colors ) {
- @signup_colors = Color::Scheme->new
- ->from_hue($hue)
- ->scheme('analogic')
- ->colors;
- $hue += $hue_increment;
+ # rotate hue for each referral type
+ @signup_colors = Color::Scheme->new->from_hue($hue)->colors;
+ $hue += $hue_increment;
+ push @colors, $signup_colors[0];
+ if ( $indirect ) {
+ push @items, 'signups';
+ push @labels, $all_referral ?
+ 'Referrals' :
+ $referral->referral . ' referrals';
+ push @params, [ %params, 'indirect' => 1 ];
+ push @links, '';
+ push @colors, $signup_colors[1];
}
- push @colors, shift @signup_colors;
}
</%init>
diff --git a/httemplate/graph/report_cust_signup.html b/httemplate/graph/report_cust_signup.html
index 9d3f5006b..12dec8e6a 100644
--- a/httemplate/graph/report_cust_signup.html
+++ b/httemplate/graph/report_cust_signup.html
@@ -22,6 +22,12 @@
)
%>
+<& /elements/tr-td-label.html, label => 'Show customer referrals' &>
+<TD>
+ <INPUT TYPE="checkbox" NAME="indirect" VALUE="Y">
+</TD>
+</TR>
+
</TABLE>
<BR><INPUT TYPE="submit" VALUE="Display">
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
index aae8c7e99..859ef04e6 100755
--- a/httemplate/search/cust_main.cgi
+++ b/httemplate/search/cust_main.cgi
@@ -334,7 +334,7 @@
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('List customers');
+ unless $curuser->access_right('List all customers');
my $conf = new FS::Conf;
my $maxrecords = $conf->config('maxsearchrecordsperpage');
diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html
index af0c8fc09..53167c26e 100644
--- a/httemplate/search/elements/search-html.html
+++ b/httemplate/search/elements/search-html.html
@@ -130,7 +130,9 @@
</TD>
-% unless ( $opt{'disable_download'} || $type eq 'html-print' ) {
+% if ( $curuser->access_right('Download report data')
+% and !$opt{'disable_download'}
+% and $type ne 'html-print' ) {
<TD ALIGN="right">
@@ -470,6 +472,8 @@
% }
<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
my %args = @_;
my $type = $args{'type'};
my $header = $args{'header'};
diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html
index 81ec4d082..9bc66b6fa 100644
--- a/httemplate/search/elements/search.html
+++ b/httemplate/search/elements/search.html
@@ -170,7 +170,6 @@ Example:
%
<% include('search-csv.html', header=>$header, rows=>$rows, opt=>\%opt ) %>
%
-% #} elsif ( $type eq 'excel' ) {
% } elsif ( $type =~ /\.xls$/ ) {
%
<% include('search-xls.html', header=>$header, rows=>$rows, opt=>\%opt ) %>
@@ -179,7 +178,7 @@ Example:
%
<% include('search-xml.html', rows=>$rows, opt=>\%opt ) %>
%
-% } else { # regular HTML
+% } else {
%
<% include('search-html.html',
type => $type,
@@ -205,6 +204,11 @@ my $curuser = $FS::CurrentUser::CurrentUser;
my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|xml|select|html(-print)?)$/
? $1 : 'html' ;
+if ( !$curuser->access_right('Download report data') ) {
+ $opt{'disable_download'} = 1;
+ $type = 'html';
+}
+
my %align = (
'l' => 'left',
'r' => 'right',
@@ -363,6 +367,8 @@ unless ( $type =~ /^(csv|\w*.xls)$/) {
$maxrecords ||= $confmax;
}
+ $opt{'disable_maxselect'} ||= $conf->exists('disable_maxselect');
+
$limit = $maxrecords ? "LIMIT $maxrecords" : '';
$offset = $cgi->param('offset') =~ /^(\d+)$/ ? $1 : 0;
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index b5b716199..c453ffadc 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -13,6 +13,7 @@
'cust_main' => $cust_main,
'actionlabel' => emt('Enter check payment'),
'width' => 392,
+ 'height' => 392,
&>
% }
@@ -24,6 +25,7 @@
'cust_main' => $cust_main,
'actionlabel' => emt('Enter cash payment'),
'width' => 392,
+ 'height' => 392,
&>
% }
diff --git a/httemplate/view/cust_pay.html b/httemplate/view/cust_pay.html
index d02f1543d..f9c8bc19c 100644
--- a/httemplate/view/cust_pay.html
+++ b/httemplate/view/cust_pay.html
@@ -98,6 +98,28 @@
% }
+% if ( $cust_pay->payby eq 'CASH' && $cust_pay->payinfo ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Bank') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->bank %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Teller #') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->teller %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Depositor') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->depositor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Account #') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->account %></B></TD>
+ </TR>
+% }
+
% if ( $conf->exists('pkg-balances') && $cust_pay->pkgnum ) {
% my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_pay->pkgnum } );
<TR>