diff options
-rw-r--r-- | FS/FS/Misc.pm | 2 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 1 | ||||
-rw-r--r-- | FS/FS/Template_Mixin.pm | 3 | ||||
-rw-r--r-- | FS/FS/cdr.pm | 31 | ||||
-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 | 123 | ||||
-rw-r--r-- | FS/FS/part_tag.pm | 33 | ||||
-rwxr-xr-x | FS/bin/freeside-wkhtmltopdf | 7 | ||||
-rw-r--r-- | httemplate/edit/contact_class.html | 2 | ||||
-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_svc_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 |
23 files changed, 457 insertions, 97 deletions
diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm index 297e39fbc..a1c15fdf8 100644 --- a/FS/FS/Misc.pm +++ b/FS/FS/Misc.pm @@ -799,7 +799,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 6ad4b742d..fb1f1d69b 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1349,6 +1349,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/Template_Mixin.pm b/FS/FS/Template_Mixin.pm index d35fd55f2..146e95f1c 100644 --- a/FS/FS/Template_Mixin.pm +++ b/FS/FS/Template_Mixin.pm @@ -2251,7 +2251,8 @@ sub _items_cust_bill_pkg { $cust_pkg->h_labels_short($self->_date, undef, 'I') unless $cust_bill_pkg->pkgpart_override; #don't redisplay services - if ( $cust_pkg->locationnum != $cust_main->ship_locationnum ) { + if ( ! $cust_pkg->locationnum or + $cust_pkg->locationnum != $cust_main->ship_locationnum ) { my $loc = $cust_pkg->location_label; $loc = substr($loc, 0, $maxlength). '...' if $format eq 'latex' && length($loc) > $maxlength; diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index 05179f264..fdec921ee 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -773,11 +773,16 @@ sub rate_prefix { my $seconds_left = $part_pkg->option_cacheable('use_duration') ? $self->duration : $self->billsec; - # charge for the first (conn_sec) seconds - my $seconds = min($seconds_left, $rate_detail->conn_sec); - $seconds_left -= $seconds; - $weektime += $seconds; - my $charge = $rate_detail->conn_charge; + + #no, do this later so it respects (group) included minutes + # # charge for the first (conn_sec) seconds + # my $seconds = min($seconds_left, $rate_detail->conn_sec); + # $seconds_left -= $seconds; + # $weektime += $seconds; + # my $charge = $rate_detail->conn_charge; + my $seconds = 0; + my $charge = 0; + my $connection_charged = 0; my $etime; while($seconds_left) { @@ -840,6 +845,7 @@ sub rate_prefix { $seconds += $charge_sec; + my $region_group = ($part_pkg->option_cacheable('min_included') || 0) > 0; ${$opt{region_group_included_min}} -= $minutes @@ -853,10 +859,21 @@ sub rate_prefix { ) ) { + + #NOW do connection charges here... right? + #my $conn_seconds = min($seconds_left, $rate_detail->conn_sec); + my $conn_seconds = 0; + unless ( $connection_charged++ ) { #only one connection charge + $conn_seconds = min($charge_sec, $rate_detail->conn_sec); + $seconds_left -= $conn_seconds; + $weektime += $conn_seconds; + $charge += $rate_detail->conn_charge; + } + #should preserve (display?) this - my $charge_min = 0 - $included_min->{$regionnum}{$ratetimenum}; + my $charge_min = 0 - $included_min->{$regionnum}{$ratetimenum} - ( $conn_seconds / 60 ); $included_min->{$regionnum}{$ratetimenum} = 0; - $charge += ($rate_detail->min_charge * $charge_min); #still not rounded + $charge += ($rate_detail->min_charge * $charge_min) if $charge_min > 0; #still not rounded } elsif ( ${$opt{region_group_included_min}} > 0 && $region_group 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 cb0705041..d8ccdd0bf 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 @@ -500,7 +507,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 cac7fe572..e38346a66 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) }; + # helper sub for usage-related messages my $usage_warning = sub { my $svc = shift; @@ -483,6 +542,7 @@ sub substitutions { signupdate dundate packages recurdates ), + [ invoicing_email => sub { shift->invoicing_list_emailonly_scalar } ], #compatibility: obsolete ship_ fields - use the non-ship versions map ( { my $field = $_; @@ -520,6 +580,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')) } ], @@ -529,6 +591,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 3070f281a..4c896b422 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 ); @@ -189,18 +190,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..d46a996ca 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' }, ; @@ -235,6 +234,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 +249,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 +285,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 100755 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/contact_class.html b/httemplate/edit/contact_class.html index 1ab52e5d6..1a1e60d43 100644 --- a/httemplate/edit/contact_class.html +++ b/httemplate/edit/contact_class.html @@ -1,5 +1,5 @@ <% include( 'elements/class_Common.html', - 'name' => 'Contact Type', + 'name_singular' => 'Contact Type', 'table' => 'contact_class', 'nocat' => 1, 'addl_labels' => { 'classnum' => 'Type number', diff --git a/httemplate/edit/cust_class.html b/httemplate/edit/cust_class.html index 8fce90588..2b074aaa1 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', 'addl_fields' => \@addl_fields, 'addl_labels' => { 'tax' => 'Tax Exempt' }, diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index e3e812f19..2628b4e01 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -315,6 +315,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_svc_class.html b/httemplate/edit/part_svc_class.html index 0d9a00727..7832bd441 100644 --- a/httemplate/edit/part_svc_class.html +++ b/httemplate/edit/part_svc_class.html @@ -1,5 +1,5 @@ <% include( 'elements/class_Common.html', - 'name' => 'Service class', + 'name_singular' => 'Service class', 'table' => 'part_svc_class', 'nocat' => 1, ) diff --git a/httemplate/edit/part_tag.html b/httemplate/edit/part_tag.html index 2caeb27a0..5712560c1 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 c4e3d8ac5..95c6f3082 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, |