contacts can be shared among customers / "duplicate contact emails", RT#27943
authorIvan Kohler <ivan@freeside.biz>
Tue, 3 Feb 2015 15:18:45 +0000 (07:18 -0800)
committerIvan Kohler <ivan@freeside.biz>
Tue, 3 Feb 2015 15:18:45 +0000 (07:18 -0800)
23 files changed:
FS/FS/Daemon/Preforking.pm
FS/FS/Record.pm
FS/FS/contact.pm
FS/FS/part_event/Condition/cust_bill_has_service.pm
FS/FS/part_event/Condition/has_cust_tag.pm
FS/FS/part_export/amazon_ec2.pm
FS/FS/part_export/cardfortress.pm
FS/FS/part_svc.pm
FS/FS/phone_avail.pm
FS/FS/svc_phone.pm
FS/bin/freeside-cdrd
bin/cust_bill-credit_ship2
eg/table_template.pm
fs_selfservice/DEPLOY
httemplate/browse/discount.html
httemplate/docs/about.html
httemplate/edit/cust_main-contacts.html
httemplate/edit/elements/part_svc_column.html
httemplate/edit/quick-charge.html
httemplate/elements/popup_link.html
httemplate/elements/tr-fixed.html
httemplate/elements/tr-select-cust_location.html
httemplate/search/cust_msg.html

index 98b4fa6..f3a39a6 100644 (file)
@@ -96,6 +96,7 @@ sub daemon_run {
   #parent doesn't need to hold a DB connection open
   dbh->disconnect;
   undef $FS::UID::dbh;
+  undef $RT::Handle;
 
   server_spawn(MAX_PROCESSES);
   POE::Kernel->run();
index f8282c0..92fb896 100644 (file)
@@ -876,6 +876,7 @@ sub qsearchs { # $result_record = &FS::Record:qsearchs('table',\%hash);
   my $table = $_[0];
   my(@result) = qsearch(@_);
   cluck "warning: Multiple records in scalar search ($table)"
+        #.join(' / ', map "$_=>".$_[1]->{$_}, keys %{ $_[1] } )
     if scalar(@result) > 1;
   #should warn more vehemently if the search was on a primary key?
   scalar(@result) ? ($result[0]) : ();
index 07458c7..589fc7c 100644 (file)
@@ -464,6 +464,8 @@ sub replace {
       next;
     }
 
+    $contact_phone ||= new FS::contact_phone \%cp;
+
     my %cpd = _parse_phonestring( $self->get($pf) );
     $contact_phone->set( $_ => $cpd{$_} ) foreach keys %cpd;
 
index 6e981ee..898b08d 100644 (file)
@@ -44,13 +44,13 @@ sub condition_sql {
   my $servicenums =
     $class->condition_sql_option_option_integer('has_service');
 
-  my $sql = qq| 0 < ( SELECT COUNT(cs.svcpart)
+  my $sql = " 0 < ( SELECT COUNT(cs.svcpart)
      FROM cust_bill_pkg cbp, cust_svc cs
     WHERE cbp.invnum = cust_bill.invnum
       AND cs.pkgnum = cbp.pkgnum
       AND cs.svcpart IN $servicenums
   )
-  |;
+  ";
   return $sql;
 }
 
index cde9338..79bf2d3 100644 (file)
@@ -16,7 +16,6 @@ sub eventtable_hashref {
     };
 }
 
-#something like this
 sub option_fields {
   (
     'tagnum'  => { 'label'    => 'Customer tag',
index 06e2c23..c1082a8 100644 (file)
@@ -8,10 +8,12 @@ use FS::Record qw( qsearchs );
 use FS::svc_external;
 
 tie my %options, 'Tie::IxHash',
-  'access_key' => { label => 'AWS access key', },
-  'secret_key' => { label => 'AWS secret key', },
-  'ami'        => { label => 'AMI', 'default' => 'ami-ff46a796', },
-  'keyname'    => { label => 'Keypair name', },
+  'access_key'   => { label => 'AWS access key', },
+  'secret_key'   => { label => 'AWS secret key', },
+  'ami'          => { label => 'AMI', 'default' => 'ami-ff46a796', },
+  'keyname'      => { label => 'Keypair name', },
+  'region'       => { label => 'Region', },
+  'InstanceType' => { label => 'Instance Type', },
   #option to turn off (or on) ip address allocation
 ;
 
@@ -38,6 +40,7 @@ sub _export_insert {
     $svc_external->svcnum,
     $self->option('ami'),
     $self->option('keyname'),
+    $self->option('InstanceType'),
   );
   ref($err_or_queue) ? '' : $err_or_queue;
 }
@@ -96,31 +99,35 @@ sub amazon_ec2_queue {
   };
   $queue->insert( $self->option('access_key'),
                   $self->option('secret_key'),
+                  $self->option('region'),
                   @_
                 )
     or $queue;
 }
 
 sub amazon_ec2_new {
-  my( $access_key, $secret_key, @rest ) = @_;
+  my( $access_key, $secret_key, $region, @rest ) = @_;
 
   eval 'use Net::Amazon::EC2;';
   die $@ if $@;
 
   my $ec2 = new Net::Amazon::EC2 'AWSAccessKeyId'  => $access_key,
-                                 'SecretAccessKey' => $secret_key;
-
+                                 'SecretAccessKey' => $secret_key,
+                                 'region'          => $region || 'us-east-1',
+                                ;
   ( $ec2, @rest );
 }
 
 sub amazon_ec2_insert { #subroutine, not method
-  my( $ec2, $svcnum, $ami, $keyname ) = amazon_ec2_new(@_);
-
-  my $reservation_info = $ec2->run_instances( 'ImageId'  => $ami,
-                                              'KeyName'  => $keyname,
-                                              'MinCount' => 1,
-                                              'MaxCount' => 1,
-                                            );
+  my( $ec2, $svcnum, $ami, $keyname, $InstanceType ) = amazon_ec2_new(@_);
+
+  my $reservation_info = $ec2->run_instances(
+    'ImageId'      => $ami,
+    'KeyName'      => $keyname,
+    'InstanceType' => $InstanceType || 'm1.small',
+    'MinCount'     => 1,
+    'MaxCount'     => 1,
+  );
 
   my $instance_id = $reservation_info->instances_set->[0]->instance_id;
 
index 7ff7280..154f979 100644 (file)
@@ -28,6 +28,7 @@ sub _export_insert {
   my $ssh = Net::OpenSSH->new( $self->machine,
                                default_stdin_fh => $def_in );
 
+  #capture2 and return STDERR, its probably useful if there's a problem
   my $private_key = $ssh->capture(
     { 'stdin_data' => $svc_acct->_password. "\n" },
     '/usr/local/bin/merchant_create', map $svc_acct->$_, qw( username finger )
@@ -67,6 +68,7 @@ sub _export_delete {
   my $ssh = Net::OpenSSH->new( $self->machine,
                                default_stdin_fh => $def_in );
 
+  #capture2 and return STDERR, its probably useful if there's a problem
   my $unused_output = $ssh->capture(
     '/usr/local/bin/merchant_disable', map $svc_acct->$_, qw( username )
   );
index 2748686..f56878a 100644 (file)
@@ -697,6 +697,8 @@ some components specified by "select-.*.html", and a bunch more...
 
 =item select_label - Used with select_table, this is the field name of labels
 
+=item select_allow_empty - Used with select_table, adds an empty option
+
 =back
 
 =cut
index 52bbdeb..ae8526c 100644 (file)
@@ -283,8 +283,8 @@ sub _upgrade_data {
   my $sth = dbh->prepare(
     'UPDATE phone_avail SET svcnum = NULL
        WHERE svcnum IS NOT NULL
-         AND 0 = ( SELECT COUNT(*) FROM svc_phone
-                     WHERE phone_avail.svcnum = svc_phone.svcnum )'
+         AND NOT EXISTS ( SELECT 1 FROM svc_phone
+                            WHERE phone_avail.svcnum = svc_phone.svcnum )'
   ) or die dbh->errstr;
 
   $sth->execute or die $sth->errstr;
index 06ce948..71a61ad 100644 (file)
@@ -196,6 +196,7 @@ sub table_info {
                          select_table => 'svc_domain',
                          select_key   => 'svcnum',
                          select_label => 'domain',
+                         select_allow_empty => 1,
                          disable_inventory => 1,
                        },
         'circuit_svcnum'   => { label             => 'Circuit',
index 45d5878..a3c67f9 100644 (file)
@@ -120,10 +120,10 @@ while (1) {
 sub _shouldrun {
 
   my $extra_sql =
-    ' AND 0 < ( SELECT COUNT(*) FROM cust_pkg
-                  WHERE cust_pkg.pkgpart = part_pkg.pkgpart
-                    AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
-              )
+    ' AND EXISTS ( SELECT 1 FROM cust_pkg
+                     WHERE cust_pkg.pkgpart = part_pkg.pkgpart
+                       AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+                 )
     ';
 
   my @part_pkg =
index c4d5169..a9a899c 100755 (executable)
@@ -193,16 +193,17 @@ foreach my $cust_bill ( @cust_bill ) {
   my $cur_cr = 0;
   $cur_cr += $_->amount foreach $cust_bill->cust_credited;
   $cur_cr = '' if $cur_cr == 0;
+
+  next if $cur_cr > 0 && $opt_k;
+
   if ( $opt_p ) {
     #print $cust_bill->invnum. ','. $cust_bill->custnum. ",$tax,$credit,$cr_percent%\n";
+#    print $cust_bill->invnum. ','. $cust_bill->custnum. ',"'.
+#          $cust_bill->cust_main->name. '",'. "$tax,$credit,$cur_cr\n";
     print $cust_bill->invnum. ','. $cust_bill->custnum. ',"'.
-          $cust_bill->cust_main->name. '",'. "$tax,$credit,$cur_cr\n";
+          $cust_bill->cust_main->name. '",'. "$tax,$credit\n";
   }
 
-  next if $cur_cr > 0 && $opt_k;
-
-#COMMENTING OUT ALL DANGEROUS STUFF
-#
 #  if ( $opt_m && ! $opt_r ) {
 #
 #    my $msg_template = qsearchs('msg_template', { 'msgnum' => $opt_m } )
@@ -216,28 +217,28 @@ foreach my $cust_bill ( @cust_bill ) {
 #           " custnum ". $cust_bill->custnum. ": $error\n";
 #    }
 #  }
-#
-#  if ( $opt_c ) {
-#    my $cust_credit = new FS::cust_credit {
-#      'custnum'   => $cust_main->custnum,
-#      'amount'    => $credit,
-#      'reasonnum' => $opt_c,
-#    };
-#    my $error = $cust_credit->insert;
-#    if ( $error ) {
-#      warn "error inserting credit: $error\n";
-#    }
-#    my $cust_credit_bill = new FS::cust_credit_bill {
-#      'crednum' => $cust_credit->crednum,
-#      'invnum'  => $cust_bill->invnum,
-#      'amount'  => $credit,
-#    };
-#    my $aerror = $cust_credit_bill->insert;
-#    if ( $aerror ) {
-#      warn "error applying credit to invnum ". $cust_bill->invnum. ": $aerror\n";
-#    }
-#  }
-#
+
+  if ( $opt_c ) {
+    my $cust_credit = new FS::cust_credit {
+      'custnum'   => $cust_main->custnum,
+      'amount'    => $credit,
+      'reasonnum' => $opt_c,
+    };
+    my $error = $cust_credit->insert;
+    if ( $error ) {
+      warn "error inserting credit: $error\n";
+    }
+    my $cust_credit_bill = new FS::cust_credit_bill {
+      'crednum' => $cust_credit->crednum,
+      'invnum'  => $cust_bill->invnum,
+      'amount'  => $credit,
+    };
+    my $aerror = $cust_credit_bill->insert;
+    if ( $aerror ) {
+      warn "error applying credit to invnum ". $cust_bill->invnum. ": $aerror\n";
+    }
+  }
+
 #  if ( $opt_e && ! $opt_r ) {
 #    eval { $cust_bill->email };
 #    if ( $@ ) {
index 0a6f851..686bef6 100644 (file)
@@ -1,7 +1,7 @@
 package FS::table_name;
+use base qw( FS::Record );
 
 use strict;
-use base qw( FS::Record );
 use FS::Record qw( qsearch qsearchs );
 
 =head1 NAME
index bedb5ec..4e0f495 100755 (executable)
@@ -11,7 +11,8 @@ perl Makefile.PL && make && make install
 cd ..
 
 #( cd ..; make deploy; cd fs_selfservice )
-( cd ..; make clean; make configure-rt; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
+#( cd ..; make clean; make configure-rt; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
+( cd ..; make clean; make configure-rt; make install-perl-modules; make deploy; cd fs_selfservice )
 
 #cp /home/ivan/freeside/fs_selfservice/FS-SelfService/cgi/* /var/www/MyAccount
 #chown freeside /var/www/MyAccount/*.cgi
index d3cf873..9b2298a 100644 (file)
@@ -8,8 +8,9 @@
                  'count_query' => 'SELECT COUNT(*) FROM discount',
                  'disableable' => 1,
                  'disabled_statuspos' => 1,
-                 'header'      => [ 'Name', 'Class', 'Discount', ],
+                 'header'      => [ 'Name', 'Comment', 'Class', 'Discount', ],
                  'fields'      => [ 'name',
+                                    'comment',
                                     'classname',
                                     'description',
                                   ],
index 80d9488..0f173f2 100644 (file)
@@ -56,7 +56,7 @@ GNU <b>Affero</b> General Public License.<BR>
 
 % unless ( $agentnum ) {
   <CENTER>
-  <FONT SIZE="-3">"" - R. Hunter</FONT>
+  <FONT SIZE="-3">"Half the world's a desert / Cannibals eat human brains for dessert" - D. Zero</FONT>
   </CENTER>
 % }
 
index 9f06546..3b7eb07 100644 (file)
@@ -11,6 +11,7 @@
        { 'field'             => 'contactnum',
          'type'              => 'contact',
          'colspan'           => 6,
+         'custnum'           => $custnum,
          'm2m_method'        => 'cust_contact',
          'm2m_dstcol'        => 'contactnum',   
          'm2_label'          => ' ', #'Contact',
index 53cda85..2bb4f5e 100644 (file)
@@ -140,7 +140,8 @@ that field.
           'value_col'   => $def->{'select_key'},
           'order_by'    => dbdef->table($def->{'select_table'})->primary_key,
           'multiple'    => $def->{'multiple'},
-          'disable_empty' => 1,
+          'disable_empty' => $def->{'select_allow_empty'} ? undef : 1,
+          'empty_label' => $def->{'select_allow_empty'} ? ' ' : undef,
           'curr_value'  => $value,
           # these can be switched between multiple and singular,
           # so put the complete curr_value in an attribute
index 83620a9..58c1b0a 100644 (file)
@@ -171,6 +171,7 @@ function bill_now_changed (what) {
       &>
 %   }
 
+%              unless ($billed) {
 <TR>
   <TD ALIGN="right"><% mt('Tax exempt') |h %> </TD>
   <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
@@ -179,6 +180,7 @@ function bill_now_changed (what) {
 <& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass')  &>
 
 <& /elements/tr-select-taxproduct.html, 'label' => emt('Tax product'), 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $part_pkg->get('taxproductnum')  &>
+%              }
 
 % } else { # new one-time charge
 
index e5f8c61..2b6b187 100644 (file)
@@ -2,9 +2,9 @@
 
 Example:
 
-  include('/elements/init_overlib.html')
+  <& /elements/init_overlib.html &>
 
-  include( '/elements/popup_link.html', { #hashref or a list, either way is fine
+  <& /elements/popup_link.html', { #hashref or a list, either way is fine
 
     #required
     'action'         => 'content.html', # uri for content of popup
@@ -23,7 +23,8 @@ Example:
     'aname'          => "target", # link NAME= value, useful for #targets
     'target'         => '_parent',
     'style'          => 'css-attribute:value',
-  } )
+  }
+  &>
 
 </%doc>
 % if ($params->{'action'} && $label) {
index 6904e3b..373c0ab 100644 (file)
@@ -1,6 +1,6 @@
 <% include('tr-td-label.html', @_ ) %>
 
-  <TD BGCOLOR="#dddddd" <% $style %>><% $value %></TD>
+  <TD BGCOLOR="#dddddd" <% $style %> <% $colspan %>><% $value %></TD>
 
 </TR>
 
@@ -10,7 +10,9 @@
 
 my %opt = @_;
 
-my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+my $style = $opt{'cell_style'} ? ' STYLE="'. $opt{'cell_style'}. '" ' : '';
+
+my $colspan = $opt{'colspan'} ? ' COLSPAN="'. $opt{'colspan'}. '" ' : '';
 
 my $value = $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'};
 $value = $opt{'prefix'} . $value if defined($opt{'prefix'});
index abaaa5b..7a5b43b 100644 (file)
@@ -287,6 +287,8 @@ if ( $locationnum && $locationnum > 0 ) {
 $cust_location->coord_auto('Y');
 
 my $location_sort = sub {
+  #enabled w/label_prefix _location #    $a->locationname cmp $b->locationname
+                                    # or 
         $a->country   cmp $b->country
   or lc($a->city)     cmp lc($b->city)
   or lc($a->address1) cmp lc($b->address1)
index 486c7b0..d5b865c 100644 (file)
@@ -47,7 +47,7 @@
                           ],
        'html_init'     => $html_init,
        'really_disable_download' => 1,
-       @_
+       @_ #why?
 &>
 <%init>
 #hmm...
@@ -71,7 +71,7 @@ if ( $cgi->param('msgtype') =~ /^(\w+)$/ ) {
   push @where, "msgtype = '$1'";
 }
 if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
-  push @where, "custnum = $1";
+  push @where, "cust_msg.custnum = $1";
 }
 my ($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, '');
 push @where, "(_date >= $beginning AND _date <= $ending)";