+ my @fields = qw( last first company );
+# push @fields, map "ship_$_", @fields;
+
+ cust_header(@_) if( @_ or !@cust_fields );
+ #inefficientish, but tiny lists and only run once per page
+
+ my @location_fields;
+ foreach my $field (qw( address1 address2 city state zip latitude longitude )) {
+ foreach my $pre ('bill_','ship_') {
+ if ( grep { $_ eq $pre.$field } @cust_fields ) {
+ push @location_fields, $pre.'location.'.$field.' AS '.$pre.$field;
+ }
+ }
+ }
+ foreach my $pre ('bill_','ship_') {
+ if ( grep { $_ eq $pre.'country_full' } @cust_fields ) {
+ push @location_fields, $pre.'locationnum';
+ }
+ }
+
+ foreach my $field (qw(daytime night mobile fax )) {
+ push @fields, $field if (grep { $_ eq $field } @cust_fields);
+ }
+ push @fields, 'agent_custid';
+
+ my @extra_fields = ();
+ if (grep { $_ eq 'current_balance' } @cust_fields) {
+ push @extra_fields, FS::cust_main->balance_sql . " AS current_balance";
+ }
+
+ push @extra_fields, 'part_referral_x.referral AS referral'
+ if grep { $_ eq 'referral' } @cust_fields;
+
+ map("cust_main.$_", @fields), @location_fields, @extra_fields;
+}
+
+=item join_cust_main [ TABLE[.CUSTNUM] ] [ LOCATION_TABLE[.LOCATIONNUM] ]
+
+Returns an SQL join phrase for the FROM clause so that the fields listed
+in L<cust_sql_fields> will be available. Currently joins to cust_main
+itself, as well as cust_location (under the aliases 'bill_location' and
+'ship_location') if address fields are needed. L<cust_header()> should have
+been called already.
+
+All of these will be left joins; if you want to exclude rows with no linked
+cust_main record (or bill_location/ship_location), you can do so in the
+WHERE clause.
+
+TABLE is the table containing the custnum field. If CUSTNUM (a field name
+in that table) is specified, that field will be joined to cust_main.custnum.
+Otherwise, this function will assume the field is named "custnum". If the
+argument isn't present at all, the join will just say "USING (custnum)",
+which might work.
+
+As a special case, if TABLE is 'cust_main', only the joins to cust_location
+will be returned.
+
+LOCATION_TABLE is an optional table name to use for joining ship_location,
+in case your query also includes package information and you want the
+"service address" columns to reflect package addresses.
+
+=cut
+
+sub join_cust_main {
+ my ($cust_table, $location_table) = @_;
+ my ($custnum, $locationnum);
+ ($cust_table, $custnum) = split(/\./, $cust_table);
+ $custnum ||= 'custnum';
+ ($location_table, $locationnum) = split(/\./, $location_table);
+ $locationnum ||= 'locationnum';
+
+ my $sql = '';
+ if ( $cust_table ) {
+ $sql = " LEFT JOIN cust_main ON (cust_main.custnum = $cust_table.$custnum)"
+ unless $cust_table eq 'cust_main';
+ } else {
+ $sql = " LEFT JOIN cust_main USING (custnum)";
+ }
+
+ if ( !@cust_fields or grep /^bill_/, @cust_fields ) {
+
+ $sql .= ' LEFT JOIN cust_location bill_location'.
+ ' ON (bill_location.locationnum = cust_main.bill_locationnum)';
+
+ }
+
+ if ( !@cust_fields or grep /^ship_/, @cust_fields ) {
+
+ if (!$location_table) {
+ $location_table = 'cust_main';
+ $locationnum = 'ship_locationnum';
+ }
+
+ $sql .= ' LEFT JOIN cust_location ship_location'.
+ " ON (ship_location.locationnum = $location_table.$locationnum) ";
+ }
+
+ if ( !@cust_fields or grep { $_ eq 'referral' } @cust_fields ) {
+ $sql .= ' LEFT JOIN (select refnum, referral from part_referral) AS part_referral_x ON (cust_main.refnum = part_referral_x.refnum) ';
+ }
+
+ $sql;
+}
+
+=item cust_fields OBJECT [ CUST_FIELDS_VALUE ]
+
+Given an object that contains fields from cust_main (say, from a