diff options
| -rw-r--r-- | FS/FS/Misc.pm | 2 | ||||
| -rw-r--r-- | FS/FS/Schema.pm | 1 | ||||
| -rw-r--r-- | FS/FS/cdr/qwest.pm | 161 | ||||
| -rw-r--r-- | FS/FS/cust_bill_ApplicationCommon.pm | 38 | ||||
| -rw-r--r-- | FS/FS/msg_template.pm | 73 | ||||
| -rw-r--r-- | FS/FS/part_event/Action/letter.pm | 47 | ||||
| -rw-r--r-- | FS/FS/part_export/acct_xmlrpc.pm | 13 | ||||
| -rw-r--r-- | FS/FS/part_export/dma_radiusmanager.pm | 143 | ||||
| -rw-r--r-- | FS/FS/part_tag.pm | 33 | ||||
| -rw-r--r-- | FS/bin/freeside-wkhtmltopdf | 7 | ||||
| -rw-r--r-- | httemplate/edit/cust_class.html | 2 | ||||
| -rwxr-xr-x | httemplate/edit/cust_main.cgi | 2 | ||||
| -rw-r--r-- | httemplate/edit/cust_note_class.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/hardware_class.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/inventory_class.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/part_tag.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/pkg_class.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/process/msg_template.html | 2 | ||||
| -rw-r--r-- | httemplate/elements/tr-select-cust_tag.html | 2 |
19 files changed, 438 insertions, 98 deletions
diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm index 0da7d43dc..dfdf8eb79 100644 --- a/FS/FS/Misc.pm +++ b/FS/FS/Misc.pm @@ -801,7 +801,7 @@ sub _pslatex { } -=item print ARRAYREF +=item do_print ARRAYREF Sends the lines in ARRAYREF to the printer. diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 4e1395114..9a744efaf 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1175,6 +1175,7 @@ sub tables_hashref { 'tagname', 'varchar', '', $char_d, '', '', 'tagdesc', 'varchar', 'NULL', $char_d, '', '', 'tagcolor', 'varchar', 'NULL', 6, '', '', + 'by_default', 'char', 'NULL', 1, '', '', 'disabled', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'tagnum', diff --git a/FS/FS/cdr/qwest.pm b/FS/FS/cdr/qwest.pm new file mode 100644 index 000000000..dd2385851 --- /dev/null +++ b/FS/FS/cdr/qwest.pm @@ -0,0 +1,161 @@ +package FS::cdr::qwest; + +use strict; +use vars qw(@ISA %info); +use FS::cdr qw(_cdr_date_parser_maker); + +@ISA = qw(FS::cdr); + +my %disposition = ( + 0 => 'ANSWERED', #normal completed call + 1 => 'ANSWERED', #"treated call" + 2 => 'NO ANSWER', #abandoned call + 3 => 'ERROR', #abnormal call + 4 => 'ERROR', #signaling system error + 5 => 'ANSWERED', #forced disconnect + 6 => 'ANSWERED', #off-net route advance + 7 => 'NO ANSWER', #test call + 8 => 'NO ANSWER', #recorded promotion + 9 => 'ERROR', #TCAP DCP response time-out + 12=> 'ANSWERED', #abnormal release + 13=> 'ERROR', #"completed answer CDR"(?) + 15=> 'ERROR', #"COS failure"(?) +); + +my $startdate = _cdr_date_parser_maker('startdate'); +my $enddate = _cdr_date_parser_maker('enddate'); + +%info = ( + 'name' => 'Qwest (Standard Daily)', + 'weight' => 400, + 'type' => 'fixedlength', + 'fixedlength_format' => [qw( + billing_cycle_id:6:1:6 + discn_dt:8:7:14 + anstype:6:15:20 + pindigs:4:21:24 + origtime:6:25:30 + discn_time:6:31:36 + time_chng:1:37:37 + ani:15:38:52 + infodig:2:53:54 + calldur:11:55:65 + univacc:10:66:75 + compcode:6:76:81 + dialedno:15:82:96 + calledno:15:97:111 + predig:1:112:112 + seqnum:11:113:123 + orig_dt:8:124:131 + finsid:6:132:137 + trtmtcd:6:138:143 + anisuff:6:144:149 + origgrp:6:150:155 + origmem:6:156:161 + termgrp:6:162:167 + termmem:6:168:173 + fintkgrp:6:174:179 + billnum:24:180:203 + acctcd:12:204:215 + swid:6:216:221 + orig_bill_file_id:11:222:232 + orig_trunk_group_name:12:233:244 + orig_trunk_time_bias_ind:6:245:250 + term_trunk_group_name:12:251:262 + final_trunk_group_name:12:263:274 + orig_trunk_usage_ind:6:275:280 + orig_pricing_npa:3:281:283 + orig_pricing_nxx:3:284:286 + term_pricing_npa:3:287:289 + term_pricing_nxx:3:290:292 + prcmp_id:6:293:298 + component_group_cd:2:299:300 + component_group_val:24:301:324 + intra_lata_ind:1:325:325 + carrsel:1:326:326 + cic:6:327:332 + origlrn:10:333:342 + portedno:10:343:352 + lnpcheck:1:353:353 + )], + 'import_fields' => [ + '', # billing_cycle_id + sub { # discn_dt + # hold onto this, combine it with discn_time later + # YYYYMMDD + my ($cdr, $data, $conf, $param) = @_; + $param->{'discn_dt'} = $data; + ''; + }, + '', # anstype + '', # pindigs + sub { # orig_time + # and this + # hhmmss + my ($cdr, $data, $conf, $param) = @_; + $param->{'orig_time'} = $data; + ''; + }, + sub { # discn_time + my ($cdr, $data, $conf, $param) = @_; + $data = $param->{'discn_dt'} . $data; #YYYYMMDDhhmmss + $enddate->($cdr, $data); + }, + '', # time_chng + 'src', # ani (originating number) + '', # infodig + 'billsec', # calldur + '', # univacc + sub { # compcode + my ($cdr, $data) = @_; + my $compcode = sprintf('%d', $data); + $cdr->disposition($disposition{$compcode}); + # only those that map to ANSWERED are billable, but that should be + # set in rating options, not enforced here + ''; + }, + 'dst', # dialedno + '', # calledno (physical terminating number) + '', # predig (0/1/011 prefix) + '', # seqnum + sub { # orig_dt + # backward from the discn_ fields + my ($cdr, $data, $conf, $param) = @_; + $data .= $param->{'orig_time'}; + $startdate->($cdr, $data); + }, + '', # finsid + '', # trtmtcd + '', # anisuff + 'channel', # origgrp (orig. trunk group) + '', # origmem (belongs in channel?) + 'dstchannel', # termgrp (term. trunk group) + '', # termmem (same?) + '', # fintkgrp + 'charged_party', # billnum (empty for "normal" calls) + '', # acctcd + '', # swid + '', # orig_bill_file_id + '', # orig_trunk_group_name + '', # orig_trunk_time_bias_ind + '', # term_trunk_group_name + '', # final_trunk_group_name + '', # orig_trunk_usage_ind + '', # orig_pricing_npa + '', # orig_pricing_nxx + '', # term_pricing_npa + '', # term_pricing_nxx + '', # prcmp_id + '', # component_group_cd + '', # component_group_val + '', # intra_lata_ind (or should we use this?) + '', # carrsel + '', # cic + '', # origlrn + '', # portedno + '', # lnpcheck + ], + +); + +1; diff --git a/FS/FS/cust_bill_ApplicationCommon.pm b/FS/FS/cust_bill_ApplicationCommon.pm index cadb8a796..38efc45a2 100644 --- a/FS/FS/cust_bill_ApplicationCommon.pm +++ b/FS/FS/cust_bill_ApplicationCommon.pm @@ -1,9 +1,11 @@ package FS::cust_bill_ApplicationCommon; use strict; -use vars qw( @ISA $DEBUG $me $skip_apply_to_lineitems_hack ); +use vars qw( @ISA $DEBUG $me $skip_apply_to_lineitems_hack $date_format ); use List::Util qw(min); +use Date::Format; use FS::Schema qw( dbdef ); +use FS::UID; use FS::Record qw( qsearch qsearchs dbh ); use FS::cust_pkg; use FS::cust_svc; @@ -18,6 +20,11 @@ $me = '[FS::cust_bill_ApplicationCommon]'; $skip_apply_to_lineitems_hack = 0; +FS::UID->install_callback( sub { + my $conf = new FS::Conf; + $date_format = $conf->config('date_format') || '%x'; #/YY +} ); + =head1 NAME FS::cust_bill_ApplicationCommon - Base class for bill application classes @@ -495,7 +502,34 @@ Returns a string representing the invoice (see L<FS::cust_bill>), for example: sub applied_to_invoice { my $self = shift; - 'applied to '. $self->cust_bill->invnum_date_pretty; + my $string = 'applied to '. $self->cust_bill->invnum_date_pretty; + + #show application date if over 24 hours after (or before) payment/credit date + $string .= ' on '. $self->_date_pretty + if abs( $self->_date - $self->_app_source_object->_date ) > 86400; + + $string; +} + +=item _app_source_object + +=cut + +sub _app_source_object { + my $self = shift; + my $source_table = $self->_app_source_table; + $self->$source_table(); +} + +=item _date_pretty + +Returns a string with the application date, for example: "3/20/2008" + +=cut + +sub _date_pretty { + my $self = shift; + time2str($date_format, $self->_date); } =item lineitem_breakdown_table diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm index b88e13ae0..4d28bbc0f 100644 --- a/FS/FS/msg_template.pm +++ b/FS/FS/msg_template.pm @@ -16,6 +16,9 @@ use Date::Format qw( time2str ); use HTML::Entities qw( decode_entities encode_entities ) ; use HTML::FormatText; use HTML::TreeBuilder; + +use File::Temp; +use IPC::Run qw(run); use vars qw( $DEBUG $conf ); FS::UID->install_callback( sub { $conf = new FS::Conf; } ); @@ -273,8 +276,8 @@ A hash reference of additional substitutions sub prepare { my( $self, %opt ) = @_; - my $cust_main = $opt{'cust_main'}; - my $object = $opt{'object'}; + my $cust_main = $opt{'cust_main'} or die 'cust_main required'; + my $object = $opt{'object'} or die 'object required'; # localization my $locale = $cust_main->locale || ''; @@ -435,9 +438,65 @@ sub send { send_email(generate_email($self->prepare(@_))); } +=item render OPTION => VALUE ... + +Fills in the template and renders it to a PDF document. Returns the +name of the PDF file. + +Options are as for 'prepare', but 'from' and 'to' are meaningless. + +=cut + +# will also have options to set paper size, margins, etc. + +sub render { + my $self = shift; + eval "use PDF::WebKit"; + die $@ if $@; + my %opt = @_; + my %hash = $self->prepare(%opt); + my $html = $hash{'html_body'}; + + my $tmp = 'msg'.$self->msgnum.'-'.time2str('%Y%m%d', time).'-XXXXXXXX'; + my $dir = "$FS::UID::cache_dir/cache.$FS::UID::datasrc"; + + # Graphics/stylesheets should probably go in /var/www on the Freeside + # machine. + my $kit = PDF::WebKit->new(\$html); #%options + # hack to use our wrapper script + $kit->configure(sub { shift->wkhtmltopdf('freeside-wkhtmltopdf') }); + my $fh = File::Temp->new( + TEMPLATE => $tmp, + DIR => $dir, + UNLINK => 0, + SUFFIX => '.pdf' + ); + + print $fh $kit->to_pdf; + close $fh; + return $fh->filename; +} + +=item print OPTIONS + +Render a PDF and send it to the printer. OPTIONS are as for 'render'. + +=cut + +sub print { + my $file = render(@_); + my @lpr = $conf->config('lpr'); + run ([@lpr, '-r'], '<', $file) + or die "lpr error:\n$?\n"; +} + + # helper sub for package dates my $ymd = sub { $_[0] ? time2str('%Y-%m-%d', $_[0]) : '' }; +# helper sub for money amounts +my $money = sub { ($conf->money_char || '$') . sprintf('%.2f', $_[0] || 0) }; + #my $conf = new FS::Conf; #return contexts and fill-in values @@ -484,6 +543,7 @@ sub substitutions { [ company_address => sub { $conf->config('company_address', shift->agentnum) } ], + [ invoicing_email => sub { shift->invoicing_list_emailonly_scalar } ], [ company_phonenum => sub { $conf->config('company_phonenum', shift->agentnum) } ], @@ -499,6 +559,8 @@ sub substitutions { labels_short ), [ pkg => sub { shift->part_pkg->pkg } ], + [ pkg_category => sub { shift->part_pkg->categoryname } ], + [ pkg_class => sub { shift->part_pkg->classname } ], [ cancel => sub { shift->getfield('cancel') } ], # grrr... [ start_ymd => sub { $ymd->(shift->getfield('start_date')) } ], [ setup_ymd => sub { $ymd->(shift->getfield('setup')) } ], @@ -508,6 +570,13 @@ sub substitutions { [ susp_ymd => sub { $ymd->(shift->getfield('susp')) } ], [ expire_ymd => sub { $ymd->(shift->getfield('expire')) } ], [ cancel_ymd => sub { $ymd->(shift->getfield('cancel')) } ], + + # not necessarily correct for non-flat packages + [ setup_fee => sub { shift->part_pkg->option('setup_fee') } ], + [ recur_fee => sub { shift->part_pkg->option('recur_fee') } ], + + [ freq_pretty => sub { shift->part_pkg->freq_pretty } ], + ], 'cust_bill' => [qw( invnum diff --git a/FS/FS/part_event/Action/letter.pm b/FS/FS/part_event/Action/letter.pm new file mode 100644 index 000000000..57b7b7783 --- /dev/null +++ b/FS/FS/part_event/Action/letter.pm @@ -0,0 +1,47 @@ +package FS::part_event::Action::letter; + +use strict; +use base qw( FS::part_event::Action ); +use FS::Record qw( qsearchs ); +use FS::msg_template; + +sub description { 'Print a form letter to the customer' } + +#sub eventtable_hashref { +# { 'cust_main' => 1, +# 'cust_bill' => 1, +# 'cust_pkg' => 1, +# }; +#} + +sub option_fields { + ( + 'msgnum' => { 'label' => 'Template', + 'type' => 'select-table', + 'table' => 'msg_template', + 'name_col' => 'msgname', + 'disable_empty' => 1, + }, + ); +} + +sub default_weight { 56; } #? + +sub do_action { + my( $self, $object ) = @_; + + my $cust_main = $self->cust_main($object); + + my $msgnum = $self->option('msgnum'); + + my $msg_template = qsearchs('msg_template', { 'msgnum' => $msgnum } ) + or die "Template $msgnum not found"; + + $msg_template->print( + 'cust_main' => $cust_main, + 'object' => $object, + ); + +} + +1; diff --git a/FS/FS/part_export/acct_xmlrpc.pm b/FS/FS/part_export/acct_xmlrpc.pm index 96ad1fa67..7f0763b98 100644 --- a/FS/FS/part_export/acct_xmlrpc.pm +++ b/FS/FS/part_export/acct_xmlrpc.pm @@ -5,6 +5,7 @@ use vars qw( %info ); # $DEBUG ); #use Data::Dumper; use Tie::IxHash; use Frontier::Client; #to avoid adding a dependency on RPC::XML just now +use Frontier::RPC2; #use FS::Record qw( qsearch qsearchs ); use FS::Schema qw( dbdef ); @@ -188,18 +189,18 @@ sub _export_value { if ( $fields{$value} ) { my $type = dbdef->table('svc_acct')->column($value)->type; if ( $type =~ /^(int|serial)/i ) { - return Frontier::Client->new->int( $svc_acct->$value() ); + return Frontier::RPC2::Integer->new( $svc_acct->$value() ); } elsif ( $value =~ /^last_log/ ) { - return Frontier::Client->new->date_time( $svc_acct->$value() ); #conversion? + return Frontier::RPC2::DateTime::ISO8601->new( $svc_acct->$value() ); #conversion? } else { - return Frontier::Client->new->string( $svc_acct->$value() ); + return Frontier::RPC2::String->new( $svc_acct->$value() ); } } elsif ( $value eq 'domain' ) { - return Frontier::Client->new->string( $svc_acct->domain ); + return Frontier::RPC2::String->new( $svc_acct->domain ); } elsif ( $value eq 'crypt_password' ) { - return Frontier::Client->new->string( $svc_acct->crypt_password( $self->option('crypt') ) ); + return Frontier::RPC2::String->new( $svc_acct->crypt_password( $self->option('crypt') ) ); } elsif ( $value eq 'ldap_password' ) { - return Frontier::Client->new->string( $svc_acct->ldap_password($self->option('crypt') ) ); + return Frontier::RPC2::String->new( $svc_acct->ldap_password($self->option('crypt') ) ); } elsif ( $value eq 'radius_groups' ) { my @radius_groups = $svc_acct->radius_groups; #XXX diff --git a/FS/FS/part_export/dma_radiusmanager.pm b/FS/FS/part_export/dma_radiusmanager.pm index 6e56c996b..2ca0119ca 100644 --- a/FS/FS/part_export/dma_radiusmanager.pm +++ b/FS/FS/part_export/dma_radiusmanager.pm @@ -18,9 +18,8 @@ tie %options, 'Tie::IxHash', 'username' => { label=>'Database username' }, 'password' => { label=>'Database password' }, 'manager' => { label=>'Manager name' }, - 'groupid' => { label=>'Group ID', default=>'1' }, - 'service_prefix' => { label=>'Service name prefix' }, - 'nasnames' => { label=>'NAS IDs/addresses' }, + 'template_name' => { label=>'Template service name' }, + 'service_prefix' => { label=>'Service name prefix' }, 'debug' => { label=>'Enable debugging', type=>'checkbox' }, ; @@ -59,13 +58,12 @@ sub dma_rm_queue { my $cust_pkg = $svc_acct->cust_svc->cust_pkg; my $cust_main = $cust_pkg->cust_main; - my $location = $cust_pkg->cust_location; - my $address = $location->address1; - $address .= ' '.$location->address2 if $location->address2; - my $country = code2country($location->country); - my $lsc = Locale::SubCountry->new($location->country); - my $state = $lsc->full_name($location->state) if defined($lsc); + my $address = $cust_main->address1; + $address .= ' '.$cust_main->address2 if $cust_main->address2; + my $country = code2country($cust_main->country); + my $lsc = Locale::SubCountry->new($cust_main->country); + my $state = $lsc->full_name($cust_main->state) if defined($lsc); my %params = ( # for the remote side @@ -78,13 +76,12 @@ sub dma_rm_queue { company => $cust_main->company, phone => ($cust_main->daytime || $cust_main->night), mobile => $cust_main->mobile, - address => $location->address1, # address2? - city => $location->city, + city => $cust_main->city, state => $state, #full name - zip => $location->zip, + zip => $cust_main->zip, country => $country, #full name - gpslat => $location->latitude, - gpslong => $location->longitude, + gpslat => $cust_main->latitude, + gpslong => $cust_main->longitude, comment => 'svcnum'.$svcnum, createdby => $self->option('manager'), owner => $self->option('manager'), @@ -235,6 +232,14 @@ not, create one. Then return its srvid. sub export_part_svc { my ($self, $part_svc, $dbh) = @_; + # if $dbh exists, use the existing transaction + # otherwise create our own and commit when finished + my $commit = 0; + if (!$dbh) { + $dbh = $self->connect; + $commit = 1; + } + my $name = $self->option('service_prefix').$part_svc->svc; my %params = ( @@ -242,19 +247,22 @@ sub export_part_svc { 'enableservice' => 1, 'nextsrvid' => -1, 'dailynextsrvid' => -1, + # force price-related fields to zero + 'unitprice' => 0, + 'unitpriceadd' => 0, + 'unitpricetax' => 0, + 'unitpriceaddtax' => 0, ); my @fixed_groups; # use speed settings from fixed usergroups configured on this part_svc if ( my $psc = $part_svc->part_svc_column('usergroup') ) { - if ( $psc->columnflag eq 'F' ) { - # each part_svc really should only have one fixed group with non-null - # speed settings, but go by priority order for consistency - @fixed_groups = - sort { $a->priority <=> $b->priority } - grep { $_ } - map { FS::radius_group->by_key($_) } - split(/\s*,\s*/, $psc->columnvalue); - } + # each part_svc really should only have one fixed group with non-null + # speed settings, but go by priority order for consistency + @fixed_groups = + sort { $a->priority <=> $b->priority } + grep { $_ } + map { FS::radius_group->by_key($_) } + split(/\s*,\s*/, $psc->columnvalue); } # otherwise there are no fixed groups, so leave speed empty foreach (qw(down up)) { @@ -275,76 +283,71 @@ sub export_part_svc { $sth->execute($name) or die $dbh->errstr; if ( $sth->rows > 1 ) { die "Multiple services with name '$name' found in Radius Manager.\n"; - } elsif ( $sth->rows == 1 ) { - my $row = $sth->fetchrow_arrayref; - my $srvid = $row->[0]; - warn "rm_services: updating srvid#$srvid\n" if $DEBUG; - $sth = $dbh->prepare( - 'UPDATE rm_services SET '.join(', ', map {"$_ = ?"} keys %params) . - ' WHERE srvid = ?' - ); - $sth->execute(values(%params), $srvid) or die $dbh->errstr; - return $srvid; - } else { # $sth->rows == 0 - # create a new one - # but first... get the next available srvid + + } elsif ( $sth->rows == 0 ) { + # leave this blank to disable creating new service defs + my $template_name = $self->option('template_name'); + + die "Can't create a new service profile--no template service specified.\n" + unless $template_name; + + warn "rm_services: fetching template '$template_name'\n" if $DEBUG; + $sth = $dbh->prepare('SELECT * FROM rm_services WHERE srvname = ? LIMIT 1'); + $sth->execute($template_name); + die "Can't create a new service profile--template service ". + "'$template_name' not found.\n" unless $sth->rows == 1; + my $template = $sth->fetchrow_hashref; + %params = (%$template, %params); + + # get the next available srvid $sth = $dbh->prepare('SELECT MAX(srvid) FROM rm_services'); $sth->execute or die $dbh->errstr; - my $srvid = 1; # just in case you somehow have nothing in your database + my $srvid; if ( $sth->rows ) { $srvid = $sth->fetchrow_arrayref->[0] + 1; } $params{'srvid'} = $srvid; - # NOW create a new one + + # create a new one based on the template warn "rm_services: inserting '$name' as srvid#$srvid\n" if $DEBUG; $sth = $dbh->prepare( 'INSERT INTO rm_services ('.join(', ', keys %params). ') VALUES ('.join(', ', map {'?'} keys %params).')' ); $sth->execute(values(%params)) or die $dbh->errstr; - # also link it to our manager name + # also link it to all the managers allowed on the template service warn "rm_services: linking to manager\n" if $DEBUG; $sth = $dbh->prepare( - 'INSERT INTO rm_allowedmanagers (srvid, managername) VALUES (?, ?)' + 'INSERT INTO rm_allowedmanagers (srvid, managername) '. + 'SELECT ?, managername FROM rm_allowedmanagers WHERE srvid = ?' ); - $sth->execute($srvid, $self->option('manager')) or die $dbh->errstr; - # and allow it on our NAS + $sth->execute($srvid, $template->{srvid}) or die $dbh->errstr; + # and the same for NASes + warn "rm_services: linking to nas\n" if $DEBUG; $sth = $dbh->prepare( - 'INSERT INTO rm_allowednases (srvid, nasid) VALUES (?, ?)' + 'INSERT INTO rm_allowednases (srvid, nasid) '. + 'SELECT ?, nasid FROM rm_allowednases WHERE srvid = ?' ); - foreach my $nasid ($self->nas_ids($dbh)) { - warn "rm_services: linking to nasid#$nasid\n" if $DEBUG; - $sth->execute($srvid, $nasid) or die $dbh->errstr; - } - return $srvid; - } -} + $sth->execute($srvid, $template->{srvid}) or die $dbh->errstr; -=item nas_ids DBH + $dbh->commit if $commit; + return $srvid; -Convert the 'nasnames option into a list of real NAS ids. + } else { # $sth->rows == 1, it already exists -=cut - -sub nas_ids { - my $self = shift; - my $dbh = shift; + my $row = $sth->fetchrow_arrayref; + my $srvid = $row->[0]; + warn "rm_services: updating srvid#$srvid\n" if $DEBUG; + $sth = $dbh->prepare( + 'UPDATE rm_services SET '.join(', ', map {"$_ = ?"} keys %params) . + ' WHERE srvid = ?' + ); + $sth->execute(values(%params), $srvid) or die $dbh->errstr; - my @nasnames = split(/\s*,\s*/, $self->option('nasnames')); - return unless @nasnames; - # pass these through unchanged - my @ids = grep { /^\d+$/ } @nasnames; - @nasnames = grep { not /^\d+$/ } @nasnames; - if ( @nasnames ) { - my $in_nasnames = join(',', map {$dbh->quote($_)} @nasnames); + $dbh->commit if $commit; + return $srvid; - my $sth = $dbh->prepare("SELECT id FROM nas WHERE nasname IN ($in_nasnames)"); - $sth->execute or die $dbh->errstr; - my $rows = $sth->fetchall_arrayref; - push @ids, $_->[0] foreach @$rows; } - - return @ids; } 1; diff --git a/FS/FS/part_tag.pm b/FS/FS/part_tag.pm index 0229e3aaa..ed3192969 100644 --- a/FS/FS/part_tag.pm +++ b/FS/FS/part_tag.pm @@ -30,22 +30,17 @@ FS::Record. The following fields are currently supported: =over 4 -=item tagnum +=item tagnum - primary key -primary key +=item tagname - tag name -=item tagname +=item tagdesc - description (can be longer than name) -tagname +=item tagcolor - HTML-style color to display this tag -=item tagdesc - -tagdesc - -=item tagcolor - -tagcolor +=item by_default - 'Y' to enable this tag on new customers +=item disabled =back @@ -111,6 +106,7 @@ sub check { || $self->ut_text('tagname') || $self->ut_textn('tagdesc') || $self->ut_textn('tagcolor') + || $self->ut_enum('by_default', [ '', 'Y' ] ) || $self->ut_enum('disabled', [ '', 'Y' ] ) ; return $error if $error; @@ -120,6 +116,21 @@ sub check { =back +=head1 CLASS METHODS + +=over 4 + +=item default_tags + +Returns the tagnums of all tags that have 'by_default' enabled. + +=cut + +sub default_tags { + my $class = shift; + map { $_->tagnum } qsearch('part_tag', { disabled => '', by_default => 'Y' }); +} + =head1 BUGS =head1 SEE ALSO diff --git a/FS/bin/freeside-wkhtmltopdf b/FS/bin/freeside-wkhtmltopdf new file mode 100644 index 000000000..c6c5531a5 --- /dev/null +++ b/FS/bin/freeside-wkhtmltopdf @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ $DISPLAY ] ; then + wkhtmltopdf $@ +else + xvfb-run -- wkhtmltopdf $@ +fi diff --git a/httemplate/edit/cust_class.html b/httemplate/edit/cust_class.html index fdb58e687..f1bde8f3b 100644 --- a/httemplate/edit/cust_class.html +++ b/httemplate/edit/cust_class.html @@ -1,5 +1,5 @@ <% include( 'elements/class_Common.html', - 'name' => 'Customer Class', + 'name_singular' => 'Customer Class', 'table' => 'cust_class', ) %> diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index 91419af5a..3d9aa8002 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -318,6 +318,8 @@ if ( $cgi->param('error') ) { $stateid = ''; $payinfo = ''; + $cgi->param('tagnum', FS::part_tag->default_tags); + if ( $cgi->param('qualnum') =~ /^(\d+)$/ ) { my $qualnum = $1; my $qual = qsearchs('qual', { 'qualnum' => $qualnum } ) diff --git a/httemplate/edit/cust_note_class.html b/httemplate/edit/cust_note_class.html index 111190b71..a7e47397e 100644 --- a/httemplate/edit/cust_note_class.html +++ b/httemplate/edit/cust_note_class.html @@ -1,5 +1,5 @@ <% include( 'elements/class_Common.html', - 'name' => 'Customer Note Class', + 'name_singular' => 'Customer Note Class', 'table' => 'cust_note_class', 'nocat' => 1, ) diff --git a/httemplate/edit/hardware_class.html b/httemplate/edit/hardware_class.html index 8760dd86c..26f487dda 100644 --- a/httemplate/edit/hardware_class.html +++ b/httemplate/edit/hardware_class.html @@ -1,5 +1,5 @@ <% include( 'elements/edit.html', - 'name' => 'Hardware Class', + 'name_singular' => 'Hardware Class', 'table' => 'hardware_class', 'labels' => { 'classnum' => 'Class number', diff --git a/httemplate/edit/inventory_class.html b/httemplate/edit/inventory_class.html index 3ab47fe28..ddde55799 100644 --- a/httemplate/edit/inventory_class.html +++ b/httemplate/edit/inventory_class.html @@ -1,5 +1,5 @@ <% include( 'elements/edit.html', - 'name' => 'Inventory Class', + 'name_singular' => 'Inventory Class', 'table' => 'inventory_class', 'labels' => { 'classnum' => 'Class number', diff --git a/httemplate/edit/part_tag.html b/httemplate/edit/part_tag.html index 87c77fa98..64ecefba4 100644 --- a/httemplate/edit/part_tag.html +++ b/httemplate/edit/part_tag.html @@ -5,12 +5,14 @@ { field=>'tagname', type=>'text', size=>10 }, { field=>'disabled', type=>'checkbox', value=>'Y' }, { field=>'tagdesc', type=>'text', size=>60 }, + { field=>'by_default', type=>'checkbox', value=>'Y' }, $tagcolor, ], 'labels' => { 'tagnum' => 'Tag #', 'tagname' => 'Tag', 'tagdesc' => 'Message', 'tagcolor' => 'Highlight Color', + 'by_default' => 'On by default', 'disabled' => 'Disabled', }, 'viewall_dir' => 'browse', diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html index 1bc100e36..674afe690 100644 --- a/httemplate/edit/pkg_class.html +++ b/httemplate/edit/pkg_class.html @@ -1,5 +1,5 @@ <% include( 'elements/class_Common.html', - 'name' => 'Package Class', + 'name_singular' => 'Package Class', 'table' => 'pkg_class', %opt, ) diff --git a/httemplate/edit/process/msg_template.html b/httemplate/edit/process/msg_template.html index b19f5c542..e146adf76 100644 --- a/httemplate/edit/process/msg_template.html +++ b/httemplate/edit/process/msg_template.html @@ -29,6 +29,8 @@ sub args_callback { # no validation of these; they can contain just about anything $content{'subject'} = $cgi->param('subject') || ''; $content{'body'} = $cgi->param('body') || ''; + $object->subject(''); + $object->body(''); return %content; } diff --git a/httemplate/elements/tr-select-cust_tag.html b/httemplate/elements/tr-select-cust_tag.html index 5312644ef..76b1b715d 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 && $cgi->param('error') ) { +if ( $cgi && $cgi->param('tagnum') ) { @curr_tagnum = $cgi->param('tagnum'); } elsif ( $opt{'custnum'} ) { @curr_tagnum = map $_->tagnum, |
