use FS::access_usergroup;
use FS::agent;
use FS::cust_main;
+use FS::sales;
+use FS::sched_item;
$DEBUG = 0;
$me = '[FS::access_user]';
|| $self->ut_text('last')
|| $self->ut_text('first')
|| $self->ut_foreign_keyn('user_custnum', 'cust_main', 'custnum')
+ || $self->ut_foreign_keyn('report_salesnum', 'sales', 'salesnum')
|| $self->ut_enum('disabled', [ '', 'Y' ] )
;
return $error if $error;
qsearchs( 'cust_main', { 'custnum' => $self->user_custnum } );
}
+=item report_sales
+
+Returns the FS::sales object (see L<FS::sales>), if any, for this
+user.
+
+=cut
+
+sub report_sales {
+ my $self = shift;
+ qsearchs( 'sales', { 'salesnum' => $self->report_salesnum } );
+}
+
=item access_usergroup
Returns links to the the groups this user is a part of, as FS::access_usergroup
#
#}
+=item num_agents
+
+Returns the number of agents this user can view (via group membership).
+
+=cut
+
+sub num_agents {
+ my $self = shift;
+ $self->scalar_sql(
+ 'SELECT COUNT(DISTINCT agentnum) FROM access_usergroup
+ JOIN access_groupagent USING ( groupnum )
+ WHERE usernum = ?',
+ $self->usernum,
+ );
+}
+
=item agentnums
Returns a list of agentnums this user can view (via group membership).
if ( $self->access_right($viewall_right) ) {
push @or, "$agentnum IS NOT NULL";
} else {
- push @or, "$agentnum IN (". join(',', $self->agentnums). ')';
+ my @agentnums = $self->agentnums;
+ push @or, "$agentnum IN (". join(',', @agentnums). ')'
+ if @agentnums;
}
push @or, "$agentnum IS NULL"
Returns true if the user can view the specified agent.
+Also accepts optional hashref cache, to avoid redundant database calls.
+
=cut
sub agentnum {
- my( $self, $agentnum ) = @_;
+ my( $self, $agentnum, $cache ) = @_;
+ $cache ||= {};
+ return $cache->{$self->usernum}->{$agentnum}
+ if $cache->{$self->usernum}->{$agentnum};
my $sth = dbh->prepare(
"SELECT COUNT(*) FROM access_usergroup
JOIN access_groupagent USING ( groupnum )
WHERE usernum = ? AND agentnum = ?"
) or die dbh->errstr;
$sth->execute($self->usernum, $agentnum) or die $sth->errstr;
- $sth->fetchrow_arrayref->[0];
+ $cache->{$self->usernum}->{$agentnum} = $sth->fetchrow_arrayref->[0];
+ $sth->finish;
+ return $cache->{$self->usernum}->{$agentnum};
}
=item agents [ HASHREF | OPTION => VALUE ... ]
'table' => 'agent',
'hashref' => { disabled=>'' },
'extra_sql' => ' AND '. $self->agentnums_sql(@_),
+ 'order_by' => 'ORDER BY agent',
});
}
+=item access_users [ HASHREF | OPTION => VALUE ... ]
+
+Returns an array of FS::access_user objects, one for each non-disabled
+access_user in the system that shares an agent (via group membership) with
+the invoking object. Regardless of options and agents, will always at
+least return the invoking user and any users who have viewall_right.
+
+Accepts the following options:
+
+=over 4
+
+=item table
+
+Only return users who appear in the usernum field of this table
+
+=item disabled
+
+Include disabled users if true (defaults to false)
+
+=item viewall_right
+
+All users will be returned if the current user has the provided
+access right, regardless of agents (other filters still apply.)
+Defaults to 'View customers of all agents'
+
+=cut
+
+#Leaving undocumented until such time as this functionality is actually used
+#
+#=item null
+#
+#Users with no agents will be returned.
+#
+#=item null_right
+#
+#Users with no agents will be returned if the current user has the provided
+#access right.
+
+sub access_users {
+ my $self = shift;
+ my %opt = ref($_[0]) ? %{$_[0]} : @_;
+ my $table = $opt{'table'};
+ my $search = { 'table' => 'access_user' };
+ $search->{'hashref'} = $opt{'disabled'} ? {} : { 'disabled' => '' };
+ $search->{'addl_from'} = "INNER JOIN $table ON (access_user.usernum = $table.usernum)"
+ if $table;
+ my @access_users = qsearch($search);
+ my $viewall_right = $opt{'viewall_right'} || 'View customers of all agents';
+ return @access_users if $self->access_right($viewall_right);
+ #filter for users with agents $self can view
+ my @out;
+ my $agentnum_cache = {};
+ACCESS_USER:
+ foreach my $access_user (@access_users) {
+ # you can always view yourself, regardless of agents,
+ # and you can always view someone who can view you,
+ # since they might have affected your customers
+ if ( ($self->usernum eq $access_user->usernum)
+ || $access_user->access_right($viewall_right)
+ ) {
+ push(@out,$access_user);
+ next;
+ }
+ # if user has no agents, you need null or null_right to view
+ my @agents = $access_user->agents('viewall_right'=>'NONE'); #handled viewall_right above
+ if (!@agents) {
+ if ( $opt{'null'} ||
+ ( $opt{'null_right'} && $self->access_right($opt{'null_right'}) )
+ ) {
+ push(@out,$access_user);
+ }
+ next;
+ }
+ # otherwise, you need an agent in common
+ foreach my $agent (@agents) {
+ if ($self->agentnum($agent->agentnum,$agentnum_cache)) {
+ push(@out,$access_user);
+ next ACCESS_USER;
+ }
+ }
+ }
+ return @out;
+}
+
+=item access_users_hashref [ HASHREF | OPTION => VALUE ... ]
+
+Accepts same options as L</access_users>. Returns a hashref of
+users, with keys of usernum and values of username.
+
+=cut
+
+sub access_users_hashref {
+ my $self = shift;
+ my %access_users = map { $_->usernum => $_->username }
+ $self->access_users(@_);
+ return \%access_users;
+}
+
=item access_right RIGHTNAME | LISTREF
Given a right name or a list reference of right names, returns true if this
unless ( grep !exists($self->{_ACLcache}{$_}), @$rightname ) {
warn "$me ACL cache hit for ". join(', ', @$rightname). "\n"
if $DEBUG;
- return grep $self->{_ACLcache}{$_}, @$rightname
+ return scalar( grep $self->{_ACLcache}{$_}, @$rightname );
}
warn "$me ACL cache miss for ". join(', ', @$rightname). "\n"
}
+=item refund_rights PAYBY
+
+Accepts payment $payby (BILL,CASH,MCRD,MCHK,CARD,CHEK) and returns a
+list of the refund rights associated with that $payby.
+
+Returns empty list if $payby wasn't recognized.
+
+=cut
+
+sub refund_rights {
+ my $self = shift;
+ my $payby = shift;
+ my @rights = ();
+ push @rights, 'Post refund' if $payby =~ /^(BILL|CASH|MCRD|MCHK)$/;
+ push @rights, 'Post check refund' if $payby eq 'BILL';
+ push @rights, 'Post cash refund ' if $payby eq 'CASH';
+ push @rights, 'Refund payment' if $payby =~ /^(CARD|CHEK)$/;
+ push @rights, 'Refund credit card payment' if $payby eq 'CARD';
+ push @rights, 'Refund Echeck payment' if $payby eq 'CHEK';
+ return @rights;
+}
+
+=item refund_access_right PAYBY
+
+Returns true if user has L</access_right> for any L</refund_rights>
+for the specified payby.
+
+=cut
+
+sub refund_access_right {
+ my $self = shift;
+ my $payby = shift;
+ my @rights = $self->refund_rights($payby);
+ return '' unless @rights;
+ return $self->access_right(\@rights);
+}
+
=item default_customer_view
Returns the default customer view for this user, from the
"default_customer_view" user preference, the "cust_main-default_view" config,
-or the hardcoded default, "jumbo" (may change to "basics" in the near future).
+or the hardcoded default, "basics" (formerly "jumbo" prior to 3.0).
=cut
$self->option('default_customer_view')
|| $conf->config('cust_main-default_view')
- || 'jumbo'; #'basics' in 1.9.1?
+ || 'basics'; #s/jumbo/basics/ starting with 3.0
+
+}
+
+=item spreadsheet_format [ OVERRIDE ]
+
+Returns a hashref of this user's Excel spreadsheet download settings:
+'extension' (xls or xlsx), 'class' (Spreadsheet::WriteExcel or
+Excel::Writer::XLSX), and 'mime_type'. If OVERRIDE is 'XLS' or 'XLSX',
+use that instead of the user's setting.
+=cut
+
+# is there a better place to put this?
+my %formats = (
+ XLS => {
+ extension => '.xls',
+ class => 'Spreadsheet::WriteExcel',
+ mime_type => 'application/vnd.ms-excel',
+ },
+ XLSX => {
+ extension => '.xlsx',
+ class => 'Excel::Writer::XLSX',
+ mime_type => # it's on wikipedia, it must be true
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ }
+);
+
+sub spreadsheet_format {
+ my $self = shift;
+ my $override = shift;
+
+ my $f = $override
+ || $self->option('spreadsheet_format')
+ || $conf->config('spreadsheet_format')
+ || 'XLS';
+
+ $formats{$f};
}
=item is_system_user
fs_signup
fs_bootstrap
fs_selfserv
+ fs_api
) );
}
+sub sched_item {
+ my $self = shift;
+ qsearch( 'sched_item', { 'usernum' => $self->usernum } );
+}
+
=back
=head1 BUGS