diff options
Diffstat (limited to 'httemplate')
65 files changed, 1997 insertions, 928 deletions
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi index efaa59e00..be67338e1 100755 --- a/httemplate/browse/part_pkg.cgi +++ b/httemplate/browse/part_pkg.cgi @@ -41,9 +41,12 @@ if ( $cgi->param('active') ) { ) or die dbh->errstr; } else { - $sortby = \*pkgpart_sort; + $sortby = sub { $a->pkgpart <=> $b->pkgpart; }; } +my $conf = new FS::Conf; +my $taxclasses = $conf->exists('enable_taxclasses'); + %> <%= header("Package Definition Listing",menubar( 'Main Menu' => $p )) %> <% unless ( $cgi->param('active') ) { %> @@ -55,32 +58,36 @@ if ( $cgi->param('active') ) { <% } %> <%= $total %> package definitions -<% -if ( $cgi->param('showdisabled') ) { - $cgi->param('showdisabled', 0); - print qq!( <a href="!. $cgi->self_url. qq!">hide disabled packages</a> )!; -} else { - $cgi->param('showdisabled', 1); - print qq!( <a href="!. $cgi->self_url. qq!">show disabled packages</a> )!; -} +<% if ( $cgi->param('showdisabled') ) { $cgi->param('showdisabled', 0); %> + ( <a href="<%= $cgi->self_url %>">hide disabled packages</a> ) +<% } else { $cgi->param('showdisabled', 1); %> + ( <a href="<%= $cgi->self_url %>">show disabled packages</a> ) +<% } %> -my $colspan = $cgi->param('showdisabled') ? 2 : 3; -print &table(), <<END; +<% my $colspan = $cgi->param('showdisabled') ? 2 : 3; %> + +<%= &table() %> <TR> - <TH COLSPAN=$colspan>Package</TH> + <TH COLSPAN=<%= $colspan %>>Package</TH> <TH>Comment</TH> -END -print ' <TH><FONT SIZE=-1>Customer<BR>packages</FONT></TH>' - if $cgi->param('active'); -print <<END; +<% if ( $cgi->param('active') ) { %> + <TH><FONT SIZE=-1>Customer<BR>packages</FONT></TH> +<% } %> <TH><FONT SIZE=-1>Freq.</FONT></TH> +<% if ( $taxclasses ) { %> + <TH><FONT SIZE=-1>Taxclass</FONT></TH> +<% } %> <TH><FONT SIZE=-1>Plan</FONT></TH> <TH><FONT SIZE=-1>Data</FONT></TH> <TH>Service</TH> <TH><FONT SIZE=-1>Quan.</FONT></TH> +<% if ( dbdef->table('pkg_svc')->column('primary_svc') ) { %> + <TH><FONT SIZE=-1>Primary</FONT></TH> +<% } %> + </TR> -END +<% foreach my $part_pkg ( sort $sortby @part_pkg ) { my($hashref)=$part_pkg->hashref; my(@pkg_svc)=grep $_->getfield('quantity'), @@ -96,46 +103,49 @@ foreach my $part_pkg ( sort $sortby @part_pkg ) { $plandata = "Setup ". $hashref->{setup}. "<BR>Recur ". $hashref->{recur}; } - print <<END; +%> <TR> - <TD ROWSPAN=$rowspan><A HREF="${p}edit/part_pkg.cgi?$hashref->{pkgpart}">$hashref->{pkgpart}</A></TD> -END - - unless ( $cgi->param('showdisabled') ) { - print "<TD ROWSPAN=$rowspan>"; - print "DISABLED" if $hashref->{disabled}; - print '</TD>'; - } - - print <<END; - <TD ROWSPAN=$rowspan><A HREF="${p}edit/part_pkg.cgi?$hashref->{pkgpart}">$hashref->{pkg}</A></TD> - <TD ROWSPAN=$rowspan>$hashref->{comment}</TD> -END - if ( $cgi->param('active') ) { - print " <TD ROWSPAN=$rowspan>"; - print '<FONT COLOR="#00CC00"><B>'. - $num_active_cust_pkg{$hashref->{'pkgpart'}}. - qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=active;pkgpart=$hashref->{pkgpart}">active</A><BR>!; + <TD ROWSPAN=<%= $rowspan %>><A HREF="<%=$p%>edit/part_pkg.cgi?<%= $hashref->{pkgpart} %>"><%= $hashref->{pkgpart} %></A></TD> + +<% unless ( $cgi->param('showdisabled') ) { %> + <TD ROWSPAN=<%= $rowspan %>> + <% if ( $hashref->{disabled} ) { %> + DISABLED + <% } %> + </TD> +<% } %> - $suspended_sth->execute( $part_pkg->pkgpart ) or die $suspended_sth->errstr; - my $num_suspended = $suspended_sth->fetchrow_arrayref->[0]; - print '<FONT COLOR="#FF9900"><B>'. $num_suspended. - qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=suspended;pkgpart=$hashref->{pkgpart}">suspended</A><BR>!; + <TD ROWSPAN=<%= $rowspan %>><A HREF="<%=$p%>edit/part_pkg.cgi?<%= $hashref->{pkgpart} %>"><%= $hashref->{pkg} %></A></TD> + <TD ROWSPAN=<%= $rowspan %>><%= $hashref->{comment} %></TD> + +<% if ( $cgi->param('active') ) { %> + <TD ROWSPAN=<%= $rowspan %>> + <FONT COLOR="#00CC00"><B><%= $num_active_cust_pkg{$hashref->{'pkgpart'}} %></B></FONT> <A HREF="<%=$p%>search/cust_pkg.cgi?magic=active;pkgpart=<%= $hashref->{pkgpart} %>">active</A><BR> + + <% $suspended_sth->execute( $part_pkg->pkgpart ) + or die $suspended_sth->errstr; + my $num_suspended = $suspended_sth->fetchrow_arrayref->[0]; + %> + <FONT COLOR="#FF9900"><B><%= $num_suspended %></B></FONT> <A HREF="<%=$p%>search/cust_pkg.cgi?magic=suspended;pkgpart=<%= $hashref->{pkgpart} %>">suspended</A><BR> + + <% $canceled_sth->execute( $part_pkg->pkgpart ) + or die $canceled_sth->errstr; + my $num_canceled = $canceled_sth->fetchrow_arrayref->[0]; + %> + <FONT COLOR="#FF0000"><B><%= $num_canceled %></B></FONT> <A HREF="<%=$p%>search/cust_pkg.cgi?magic=canceled;pkgpart=<%= $hashref->{pkgpart} %>">canceled</A> + </TD> +<% } %> - $canceled_sth->execute( $part_pkg->pkgpart ) or die $canceled_sth->errstr; - my $num_canceled = $canceled_sth->fetchrow_arrayref->[0]; - print '<FONT COLOR="#FF0000"><B>'. $num_canceled. - qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=canceled;pkgpart=$hashref->{pkgpart}">canceled</A>!; + <TD ROWSPAN=<%= $rowspan %>><%= $hashref->{freq} %></TD> +<% if ( $taxclasses ) { %> + <TD ROWSPAN=<%= $rowspan %>><%= $hashref->{taxclass} || ' ' %></TD> +<% } %> - print '</TD>'; - } - print <<END; - <TD ROWSPAN=$rowspan>$hashref->{freq}</TD> - <TD ROWSPAN=$rowspan>$hashref->{plan}</TD> - <TD ROWSPAN=$rowspan>$plandata</TD> -END + <TD ROWSPAN=<%= $rowspan %>><%= $hashref->{plan} %></TD> + <TD ROWSPAN=<%= $rowspan %>><%= $plandata %></TD> +<% my($pkg_svc); my($n)=""; foreach $pkg_svc ( @pkg_svc ) { @@ -143,23 +153,20 @@ END my($part_svc) = qsearchs('part_svc',{'svcpart'=> $svcpart }); print $n,qq!<TD><A HREF="${p}edit/part_svc.cgi?$svcpart">!, $part_svc->getfield('svc'),"</A></TD><TD>", - $pkg_svc->getfield('quantity'),"</TD></TR>\n"; + $pkg_svc->getfield('quantity'),"</TD>"; + if ( dbdef->table('pkg_svc')->column('primary_svc') ) { + print '<TD>'; + print 'PRIMARY' if $pkg_svc->primary_svc =~ /^Y/i; + print '</TD>'; + } + print "</TR>\n"; $n="<TR>"; } +%> - print "</TR>"; -} - -$colspan = $cgi->param('showdisabled') ? 8 : 9; -print <<END; + </TR> +<% } %> </TABLE> </BODY> </HTML> -END - -sub pkgpart_sort { - $a->pkgpart <=> $b->pkgpart; -} - -%> diff --git a/httemplate/browse/part_referral.cgi b/httemplate/browse/part_referral.cgi index 3f59abcf5..581e01bb6 100755 --- a/httemplate/browse/part_referral.cgi +++ b/httemplate/browse/part_referral.cgi @@ -10,31 +10,46 @@ Where a customer heard about your service. Tracked for informational purposes. <% my $today = timelocal(0, 0, 0, (localtime(time))[3..5] ); - my %past; - tie %past, 'Tie::IxHash', + my %after; + tie %after, 'Tie::IxHash', 'Today' => 0, + 'Yesterday' => 86400, # 60sec * 60min * 24hrs 'Past week' => 518400, # 60sec * 60min * 24hrs * 6days 'Past 30 days' => 2505600, # 60sec * 60min * 24hrs * 29days - 'Past 60 days' => 5097600, # 60sec * 60min * 24hrs * 29days - 'Past 90 days' => 7689600, # 60sec * 60min * 24hrs * 29days + 'Past 60 days' => 5097600, # 60sec * 60min * 24hrs * 59days + 'Past 90 days' => 7689600, # 60sec * 60min * 24hrs * 89days 'Past 6 months' => 15724800, # 60sec * 60min * 24hrs * 182days 'Past year' => 31486000, # 60sec * 60min * 24hrs * 364days 'Total' => $today, ; + my %before = ( + 'Today' => 86400, # 60sec * 60min * 24hrs + 'Yesterday' => 0, + 'Past week' => 86400, # 60sec * 60min * 24hrs + 'Past 30 days' => 86400, # 60sec * 60min * 24hrs + 'Past 60 days' => 86400, # 60sec * 60min * 24hrs + 'Past 90 days' => 86400, # 60sec * 60min * 24hrs + 'Past 6 months' => 86400, # 60sec * 60min * 24hrs + 'Past year' => 86400, # 60sec * 60min * 24hrs + 'Total' => 86400, # 60sec * 60min * 24hrs + ); - my $sth = dbh->prepare("SELECT COUNT(*) FROM h_cust_main - WHERE history_action = 'insert' - AND refnum = ? - AND history_date > ? ") + my $statement = "SELECT COUNT(*) FROM h_cust_main + WHERE history_action = 'insert' + AND refnum = ? + AND history_date >= ? + AND history_date < ? + "; + my $sth = dbh->prepare($statement) or die dbh->errstr; %> <%= table() %> <TR> <TH COLSPAN=2 ROWSPAN=2>Advertising source</TH> - <TH COLSPAN=<%= scalar(keys %past) %>>Customers</TH> + <TH COLSPAN=<%= scalar(keys %after) %>>Customers</TH> </TR> -<% for my $period ( keys %past ) { %> +<% for my $period ( keys %after ) { %> <TH><FONT SIZE=-1><%= $period %></FONT></TH> <% } %> </TR> @@ -49,9 +64,11 @@ foreach my $part_referral ( sort { <%= $part_referral->refnum %></A></TD> <TD><A HREF="<%= $p %>edit/part_referral.cgi?<%= $part_referral->refnum %>"> <%= $part_referral->referral %></A></TD> - <% for my $period ( values %past ) { - $sth->execute($part_referral->refnum, $today-$period) - or die $sth->errstr; + <% for my $period ( keys %after ) { + $sth->execute( $part_referral->refnum, + $today-$after{$period}, + $today+$before{$period}, + ) or die $sth->errstr; my $number = $sth->fetchrow_arrayref->[0]; %> <TD ALIGN="right"><%= $number %></TD> @@ -59,6 +76,22 @@ foreach my $part_referral ( sort { </TR> <% } %> +<% + $statement =~ s/AND refnum = \?//; + $sth = dbh->prepare($statement) + or die dbh->errstr; +%> + <TR> + <TH COLSPAN=2>Total</TH> + <% for my $period ( keys %after ) { + $sth->execute( $today-$after{$period}, + $today+$before{$period}, + ) or die $sth->errstr; + my $number = $sth->fetchrow_arrayref->[0]; + %> + <TD ALIGN="right"><%= $number %></TD> + <% } %> + </TR> </TABLE> </BODY> </HTML> diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi index 7c83924a2..fd9ef3cf3 100755 --- a/httemplate/browse/part_svc.cgi +++ b/httemplate/browse/part_svc.cgi @@ -13,6 +13,20 @@ my @part_svc = qsearch('part_svc', \%search ); my $total = scalar(@part_svc); +my %num_active_cust_svc = (); +if ( $cgi->param('active') ) { + my $active_sth = dbh->prepare( + 'SELECT COUNT(*) FROM cust_svc WHERE svcpart = ?' + ) or die dbh->errstr; + foreach my $part_svc ( @part_svc ) { + $active_sth->execute($part_svc->svcpart) or die $active_sth->errstr; + $num_active_cust_svc{$part_svc->svcpart} = + $active_sth->fetchrow_arrayref->[0]; + } + @part_svc = sort { $num_active_cust_svc{$b->svcpart} <=> + $num_active_cust_svc{$a->svcpart} } @part_svc; +} + %> <%= header('Service Definition Listing', menubar( 'Main Menu' => $p) ) %> @@ -45,6 +59,9 @@ function part_export_areyousure(href) { <TR> <TH COLSPAN=<%= $cgi->param('showdisabled') ? 2 : 3 %>>Service</TH> <TH>Table</TH> +<% if ( $cgi->param('active') ) { %> + <TH><FONT SIZE=-1>Customer<BR>Services</FONT></TH> +<% } %> <TH>Export</TH> <TH>Field</TH> <TH COLSPAN=2>Modifier</TH> @@ -74,6 +91,11 @@ function part_export_areyousure(href) { <%= $hashref->{svc} %></A></TD> <TD ROWSPAN=<%= $rowspan %>> <%= $hashref->{svcdb} %></TD> +<% if ( $cgi->param('active') ) { %> + <TD ROWSPAN=<%= $rowspan %>> + <FONT COLOR="#00CC00"><B><%= $num_active_cust_svc{$hashref->{svcpart}} %></B></FONT> <A HREF="<%=$p%>search/<%= $hashref->{svcdb} %>.cgi?svcpart=<%= $hashref->{svcpart} %>">active</A> + </TD> +<% } %> <TD ROWSPAN=<%= $rowspan %>><%= itable() %> <% # my @part_export = diff --git a/httemplate/docs/ieak.html b/httemplate/docs/ieak.html new file mode 100644 index 000000000..00c53423c --- /dev/null +++ b/httemplate/docs/ieak.html @@ -0,0 +1,75 @@ +<pre> +this is incomplete +mostly it should be merged into signup.html and fs_signup/ieak.template + +- download and install the IEAK from + http://www.microsoft.com/windows/ieak/default.asp + +- Good examples may be found in + C:\Program Files\IEAK\toolkit\isp\server\ICW\signup\perl\signup08.pl + C:\Program Files\IEAK\toolkit\isp\server\ICW\reconfig\perl\reconfig04.pl + C:\Program Files\IEAK6\toolkit\isp\servless\basic\sample.ins + C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4567.ins + C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4568.ins + C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7890.ins + C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7891.ins + +- Full documentation on all the settings available in .INS files is + avaialble under Program Files | Microsoft IEAK 6 | IEAK Help + | Reference | Internet Settings (.ins) Files + +- Freeside will make the following substitutions before sending the file + to the user: + + { $ac } - area code of selected POP + { $exch } - exchange of selected POP + { $loc } - local part of selected POP + { $username } + { $password } + { $email_name } - first and last name + { $pkg } - package name + +- Simple example follows: + +[Entry] +Entry Name = IEAK Sample +[Phone] +Dial_As_Is = No +Phone_Number = { $exch }{ $loc } +Area_Code = { $ac } +Country_Code = 1 +Country_Id = 1 +[Server] +Type = PPP +SW_Compress = Yes +PW_Encrypt = Yes +Negotiate_TCP/IP = Yes +Disable_LCP = No +[TCP/IP] +Specity_IP_Address = No +Specity_Server_Address = No +IP_Header_Compress = Yes +Gateway_On_Remote = Yes +[User] +Name = { $username } +Passowrd = { $password } +Display_Password = Yes +[Internet_Mail] +Email_Name = { $email_name } +Email_Address = { $username }@example.com +POP_Server = mail.example.com +POP_Server_Port_Number = 110 +POP_Logon_Password = { $password } +SMTP_Server = mail.example.com +SMTP_Server_Port_Number = 25 +Install_Mail = 1 +[URL] +Help_Page = http://www.ieaksample.net/helpdesk +Home_Page = http://www.ieaksample.net +Search_Page = http://www.ieaksample,net/search +[Favorites] +IEAK Sample \\ IEAK Sample Home Page.url = http://acme.ieaksample.net/ +[Branding] +Window_Title = Internet Explorer from Acme Internet Services + +</pre> diff --git a/httemplate/docs/install.html b/httemplate/docs/install.html index f0eff0897..76c3d3b93 100644 --- a/httemplate/docs/install.html +++ b/httemplate/docs/install.html @@ -30,7 +30,7 @@ Before installing, you need: <li><a href="http://search.cpan.org/search?dist=HTML-Parser">HTML-Parser</a> <li><a href="http://search.cpan.org/search?dist=libnet">libnet</a> <li><a href="http://search.cpan.org/search?dist=Locale-Codes">Locale-Codes</a> - <li><a href="http://search.cpan.org/search?dist=Net-Whois">Net-Whois</a> + <li><a href="http://search.cpan.org/search?dist=Net-Whois-Raw">Net-Whois-Raw</a> <li><a href="http://search.cpan.org/search?dist=libwww-perl">libwww-perl</a> <li><a href="http://search.cpan.org/search?dist=Business-CreditCard">Business-CreditCard</a> <!-- <li><a href="http://search.cpan.org/search?dist=Data-ShowTable">Data-ShowTable</a> --> @@ -54,6 +54,7 @@ Before installing, you need: <li><a href="http://search.cpan.org/search?dist=HTML-Widgets-SelectLayers">HTML-Widgets-SelectLayers</a> <li><a href="http://search.cpan.org/search?dist=Storable">Storable</a> <!-- MyAccounts, maybe only for dev <li><a href="http://search.cpan.org/search?dist=Cache-Cache">Cache::Cache</a> --> + <li><a href="http://search.cpan.org/search?dist=Crypt-PasswdMD5">Crypt::PasswdMD5</a> <li><a href="http://search.cpan.org/search?dist=ApacheDBI">Apache::DBI</a> <i>(optional but recommended for better webinterface performance)</i> </ul> </ul> @@ -125,7 +126,7 @@ chown freeside /usr/local/etc/freeside/asp-global/ <font size="-1"><pre> cp htetc/global.asa /usr/local/etc/freeside/asp-global/global.asa </pre></font> - <li>Configure Apache for the Global directory and to execute .cgi files using Apache::ASP. For example: + <li>Configure Apache for the Global directory and to execute .cgi files using Apache::ASP. For example, add something like the following to your Apache httpd.conf file, adjusting for your actual paths: <font size="-1"><pre> PerlModule Apache::ASP <Directory /usr/local/apache/htdocs/freeside-asp> @@ -137,7 +138,8 @@ PerlHandler Apache::ASP $MLDBM::RemoveTaint = 1; </Perl> PerlSetVar Global /usr/local/etc/freeside/asp-global/ -PerlSetVar Debug 2 +PerlSetVar Debug 2 +PerlSetVar RequestBinaryRead Off </Directory> </pre></font> </ul></td> @@ -151,7 +153,7 @@ PerlSetVar Debug 2 <li> set an appropriate <tt>data_dir</tt>, such as <tt>/usr/local/etc/freeside/masondata</tt> </ul> - <li>Configure Apache to use the <tt>handler.pl</tt> file and to execute .cgi files using HTML::Mason. For example: + <li>Configure Apache to use the <tt>handler.pl</tt> file and to execute .cgi files using HTML::Mason. For example, add something like the following to your Apache httpd.conf file, adjusting for your actual paths: <font size="-1"><pre> PerlModule HTML::Mason <Directory /usr/local/apache/htdocs/freeside-mason> @@ -168,7 +170,7 @@ require "/usr/local/etc/freeside/handler.pl"; </tr> </table> <ul> -<li>Restrict access to this web interface - see the <a href="http://httpd.apache.org/docs/misc/FAQ.html#user-authentication">Apache documentation on user authentication</a>. For example, to configure user authentication with <a href="http://httpd.apache.org/docs/mod/mod_auth.html">mod_auth</a> (flat files): +<li>Restrict access to this web interface - see the <a href="http://httpd.apache.org/docs/misc/FAQ.html#user-authentication">Apache documentation on user authentication</a>. For example, to configure user authentication with <a href="http://httpd.apache.org/docs/mod/mod_auth.html">mod_auth</a> (flat files), add something like the following to your Apache httpd.conf file, adjusting for your actual paths: <pre> <Directory /usr/local/apache/htdocs/freeside-asp> AuthName Freeside @@ -187,12 +189,12 @@ $ <a href="man/bin/freeside-adduser.html">freeside-adduser</a> -c -h /usr/local/ $ <a href="man/bin/freeside-adduser.html">freeside-adduser</a> -h /usr/local/etc/freeside/htpasswd <i>username</i></pre></font> </ul> <i>(using other auth types, add each user to your <a href="http://httpd.apache.org/docs/misc/FAQ.html#user-authentication">Apache authentication</a> and then run: <tt>freeside-adduser <b>username</b></tt></i> - <li>As the freeside UNIX user, run <tt>bin/fs-setup <b>username</b></tt> (in the untar'ed freeside directory) to create the database tables, passing the username of a Freeside user you created above: + <li>As the freeside UNIX user, run <tt>freeside-setup <b>username</b></tt> to create the database tables, passing the username of a Freeside user you created above: <pre> $ su freeside -$ cd <b>/path/to/freeside/</b> -$ bin/fs-setup <b>username</b> +$ freeside-setup <b>username</b> </pre> + Alternately, use the -s option to enable shipping addresses: <tt>freeside-setup -s <b>username</b></tt> <li>As the freeside UNIX user, run <tt>bin/populate-msgcat <b>username</b></tt> (in the untar'ed freeside directory) to populate the message catalog, passing the username of a Freeside user you created above: <pre> $ su freeside diff --git a/httemplate/docs/man/FS/part_export/.cvs_is_on_crack b/httemplate/docs/man/FS/part_export/.cvs_is_on_crack deleted file mode 100644 index e69de29bb..000000000 --- a/httemplate/docs/man/FS/part_export/.cvs_is_on_crack +++ /dev/null diff --git a/httemplate/docs/upgrade-1.4.2.html b/httemplate/docs/upgrade-1.4.2.html index c5606b284..a24661142 100644 --- a/httemplate/docs/upgrade-1.4.2.html +++ b/httemplate/docs/upgrade-1.4.2.html @@ -7,10 +7,21 @@ <li>If migrating from less than 1.4.1, see these <a href="upgrade9.html">instructions</a> first. <li>Back up your data and current Freeside installation. <li>Install <a href="http://search.cpan.org/search?dist=Locale-SubCountry">Locale::SubCountry</a> + <li>Install <a href="http://search.cpan.org/search?dist=IPC-ShareLite">IPC::ShareLite</a> + <li>Install <a href="http://search.cpan.org/search?dist=HTML-Widgets-SelectLayers">HTML::Widgets::SelectLayers</a> 0.04. + <li>Install <a href="http://search.cpan.org/search?dist=DBIx-DBSchema">DBIx::DBSchema</a> 0.23. + <li>Install <a href="http://search.cpan.org/search?dist=DBD-Pg">DBD::Pg</a> 1.32. + <li>Install <a href="http://search.cpan.org/search?dist=Cache-Cache">Cache::Cache</a>. + <li>Install <a href="http://search.cpan.org/search?dist=Net-SSH">Net::SSH</a> 0.08. + <li>Install <a href="http://search.cpan.org/search?dist=Crypt-PasswdMD5">Crypt::PasswdMD5</a> + <li>Install <a href="http://search.cpan.org/search?dist=Net-Whois-Raw">Net::Whois::Raw</a> <li>CGI.pm minimum version 2.47 is required. You will probably need to install a current CGI.pm from CPAN if you are using Perl 5.005 or earlier. + <li>File::Temp minimum version 0.14 is required. You will probably need to install a currrent File::Temp from CPAN if you are using Perl 5.6 or earlier. + <li>If using Apache::ASP, add <code>PerlSetVar RequestBinaryRead Off</code> to your Apache configuration and make sure you are using Apache::ASP minimum version 2.55. <li>Run <code>make aspdocs</code> or <code>make masondocs</code>. <li>Copy <code>aspdocs/</code> or <code>masondocs/</code> to your web server's document space. <li>Run <code>make install-perl-modules</code>. - <li>The signup server and password server are deprecated in 1.4.2. Their functionality has been incorperated into the self-service server. Edit or reinstall your init script appropriately, and set the "signup_server-default_agentnum" and "signup_server-default_refnum" configuration options appropriately. The FS::SignupClient interface is still available as a compatibility wrapper. You should be able to continue to use your current signup.cgi. + <li>The signup server and password server are deprecated in 1.4.2. Their functionality has been incorperated into the self-service server. Edit or reinstall your init script, and set the "signup_server-default_agentnum" and "signup_server-default_refnum" configuration options. The FS::SignupClient interface is still available as a compatibility wrapper, so you should be able to continue to use your current signup.cgi. + <li>Optional: To use typeset invoices, install tetex and ghostscript, and copy conf/invoice_latex, conf/invoice_latexnotes, and conf/invoice_latexfooter to /usr/local/etc/freeside/conf.<datasrc>/ <li>Restart Apache and freeside-queued. </body> diff --git a/httemplate/docs/upgrade8.html b/httemplate/docs/upgrade8.html index cf60a8582..9ca7cb7b9 100644 --- a/httemplate/docs/upgrade8.html +++ b/httemplate/docs/upgrade8.html @@ -149,7 +149,9 @@ CREATE TABLE cust_bill_event ( eventnum int primary key, invnum int not null, eventpart int not null, - _date int not null + _date int not null, + status varchar(80) not null, + statustext text ); CREATE UNIQUE INDEX cust_bill_event1 ON cust_bill_event ( eventpart, invnum ); CREATE INDEX cust_bill_event2 ON cust_bill_event ( invnum ); diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index 177d16ba4..d3da7cc0a 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -71,7 +71,7 @@ print header("Customer $action", '', ' onUnload="myclose()"'); print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $error, "</FONT>" if $error; -print qq!<FORM ACTION="${p1}process/cust_main.cgi" METHOD=POST NAME="form1">!, +print qq!<FORM ACTION="${p1}process/cust_main.cgi" METHOD=POST NAME="form1" onSubmit="document.form1.submit.disabled=true">!, qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!, qq!Customer # !, ( $custnum ? "<B>$custnum</B>" : " (NEW)" ), @@ -191,8 +191,10 @@ END my $countrydefault = $conf->config('countrydefault') || 'US'; $cust_main->country( $countrydefault ) unless $cust_main->country; -$cust_main->state( $conf->config('statedefault') || 'CA' ) - unless $cust_main->state || $cust_main->country ne 'US'; +my $statedefault = $conf->config('statedefault') + || ($countrydefault eq 'US' ? 'CA' : ''); +$cust_main->state( $statedefault ) + unless $cust_main->state || $cust_main->country ne $countrydefault; my($county_html, $state_html, $country_html) = FS::cust_main_county::regionselector( $cust_main->county, @@ -289,8 +291,9 @@ END #false laziness with regular state $cust_main->ship_country( $countrydefault ) unless $cust_main->ship_country; - $cust_main->ship_state( $conf->config('statedefault') || 'CA' ) - unless $cust_main->ship_state || $cust_main->ship_country ne 'US'; + $cust_main->ship_state( $statedefault ) + unless $cust_main->ship_state + || $cust_main->ship_country ne $countrydefault; my($ship_county_html, $ship_state_html, $ship_country_html) = FS::cust_main_county::regionselector( $cust_main->ship_county, @@ -343,7 +346,9 @@ sub expselect { $return .= ">$_"; } $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!; - for ( 2001 .. 2037 ) { + my @t = localtime; + my $thisYear = $t[5] + 1900; + for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. 2037 ) { $return .= "<OPTION"; $return .= " SELECTED" if $_ == $y; $return .= ">$_"; @@ -428,7 +433,7 @@ if ( $payby_default eq 'HIDE' ) { my %payby = ( 'CARD' => qq!Credit card<BR>${r}<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR>${r}Exp !. expselect("CARD"). qq!<BR>${r}Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!, - 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE=""><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!, + 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE=""><BR>${r}ABA/Routing number <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!, 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!, 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><INPUT TYPE="hidden" NAME="BILL_month" VALUE="12"><INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="">!, 'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR>${r}Exp !. expselect("COMP"), @@ -444,7 +449,7 @@ if ( $payby_default eq 'HIDE' ) { my %paybychecked = ( 'CARD' => qq!Credit card<BR>${r}<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR>${r}Exp !. expselect("CARD", $cust_main->paydate). qq!<BR>${r}Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!, - 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account"><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!, + 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account"><BR>${r}ABA/Routing number <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!, 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!, 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><INPUT TYPE="hidden" NAME="BILL_month" VALUE="12"><INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!, 'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR>${r}Exp !. expselect("COMP", $cust_main->paydate), @@ -546,7 +551,7 @@ END my $otaker = $cust_main->otaker; print qq!<INPUT TYPE="hidden" NAME="otaker" VALUE="$otaker">!, - qq!<BR><INPUT TYPE="submit" VALUE="!, + qq!<BR><INPUT NAME="submit" TYPE="submit" VALUE="!, $custnum ? "Apply Changes" : "Add Customer", qq!">!, "</FORM></BODY></HTML>", ; diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi index f6ae7b299..755050bd8 100755 --- a/httemplate/edit/cust_pay.cgi +++ b/httemplate/edit/cust_pay.cgi @@ -3,51 +3,61 @@ my $conf = new FS::Conf; -my($link, $linknum, $paid, $payby, $payinfo, $quickpay); +my($link, $linknum, $paid, $payby, $payinfo, $quickpay, $_date); if ( $cgi->param('error') ) { - $link = $cgi->param('link'); - $linknum = $cgi->param('linknum'); - $paid = $cgi->param('paid'); - $payby = $cgi->param('payby'); - $payinfo = $cgi->param('payinfo'); + $link = $cgi->param('link'); + $linknum = $cgi->param('linknum'); + $paid = $cgi->param('paid'); + $payby = $cgi->param('payby'); + $payinfo = $cgi->param('payinfo'); $quickpay = $cgi->param('quickpay'); + $_date = $cgi->param('_date') ? str2time($cgi->param('_date')) : time; } elsif ($cgi->keywords) { my($query) = $cgi->keywords; $query =~ /^(\d+)$/; - $link = 'invnum'; - $linknum = $1; - $paid = ''; - $payby = 'BILL'; - $payinfo = ""; + $link = 'invnum'; + $linknum = $1; + $paid = ''; + $payby = 'BILL'; + $payinfo = ""; $quickpay = ''; + $_date = time; } elsif ( $cgi->param('custnum') =~ /^(\d+)$/ ) { - $link = 'custnum'; - $linknum = $1; - $paid = ''; - $payby = 'BILL'; - $payinfo = ''; + $link = 'custnum'; + $linknum = $1; + $paid = ''; + $payby = 'BILL'; + $payinfo = ''; $quickpay = $cgi->param('quickpay'); + $_date = time; } else { die "illegal query ". $cgi->keywords; } -my $_date = time; my $paybatch = "webui-$_date-$$-". rand() * 2**32; -my $p1 = popurl(1); -print header("Post payment", ''); +%> + +<%= header("Post payment", '') %> + +<% if ( $cgi->param('error') ) { %> +<FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT> +<BR><BR> +<% } %> + +<%= ntable("#cccccc",2) %> -print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'), - "</FONT><BR><BR>" - if $cgi->param('error'); +<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> -print <<END, ntable("#cccccc",2); - <FORM ACTION="${p1}process/cust_pay.cgi" METHOD=POST> - <INPUT TYPE="hidden" NAME="link" VALUE="$link"> - <INPUT TYPE="hidden" NAME="linknum" VALUE="$linknum"> - <INPUT TYPE="hidden" NAME="quickpay" VALUE="$quickpay"> -END +<FORM ACTION="<%= popurl(1) %>process/cust_pay.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="link" VALUE="<%= $link %>"> +<INPUT TYPE="hidden" NAME="linknum" VALUE="<%= $linknum %>"> +<INPUT TYPE="hidden" NAME="quickpay" VALUE="<%= $quickpay %>"> +<% my $custnum; if ( $link eq 'invnum' ) { @@ -94,36 +104,50 @@ if ( $link eq 'invnum' ) { } elsif ( $link eq 'custnum' ) { $custnum = $linknum; } +%> -print small_custview($custnum, $conf->config('countrydefault')); - -print qq!<INPUT TYPE="hidden" NAME="_date" VALUE="$_date">!; -print qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$payby">!; - -print '<BR><BR>Payment'. ntable("#cccccc", 2). - '<TR><TD ALIGN="right">Date</TD><TD BGCOLOR="#ffffff">'. - time2str("%D",$_date). '</TD></TR>'; - -print qq!<TR><TD ALIGN="right">Amount</TD><TD BGCOLOR="#ffffff">\$<INPUT TYPE="text" NAME="paid" VALUE="$paid" SIZE=8 MAXLENGTH=8></TD></TR>!; - -print qq!<TR><TD ALIGN="right">Payby</TD><TD BGCOLOR="#ffffff">$payby</TD></TR>!; - -#payinfo (check # now as payby="BILL" hardcoded.. what to do later?) -print qq!<TR><TD ALIGN="right">Check #</TD><TD BGCOLOR="#ffffff"><INPUT TYPE="text" NAME="payinfo" VALUE="$payinfo"></TD></TR>!; - -print qq!<TR><TD ALIGN="right">Auto-apply<BR>to invoices</TD><TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD>!; - -print "</TABLE>"; - -#paybatch -print qq!<INPUT TYPE="hidden" NAME="paybatch" VALUE="$paybatch">!; +<%= small_custview($custnum, $conf->config('countrydefault')) %> + +<INPUT TYPE="hidden" NAME="payby" VALUE="<%= $payby %>"> + +<BR><BR> +Payment +<%= ntable("#cccccc", 2) %> +<TR> + <TD ALIGN="right">Date</TD> + <TD COLSPAN=2> + <INPUT TYPE="text" NAME="_date" ID="_date_text" VALUE="<%= time2str("%m/%d/%Y %r",$_date) %>"> + <IMG SRC="../images/calendar.png" ID="_date_button" STYLE="cursor: pointer" TITLE="Select date"> + </TD> +</TR> +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "_date_text", + ifFormat: "%m/%d/%Y", + button: "_date_button", + align: "BR" + }); +</SCRIPT> +<TR> + <TD ALIGN="right">Amount</TD> + <TD BGCOLOR="#ffffff" ALIGN="right">$</TD> + <TD><INPUT TYPE="text" NAME="paid" VALUE="<%= $paid %>" SIZE=8 MAXLENGTH=8></TD> +</TR> +<TR> + <TD ALIGN="right">Check #</TD> + <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<%= $payinfo %>" SIZE=10></TD> +</TR> +<TR> + <TD ALIGN="right">Auto-apply<BR>to invoices</TD> + <TD COLSPAN=2><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD> +</TR> + +</TABLE> + +<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%= $paybatch %>"> -print <<END; <BR> <INPUT TYPE="submit" VALUE="Post payment"> </FORM> </BODY> </HTML> -END - -%> diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi index 4d0c7391f..b3d42bd96 100644 --- a/httemplate/edit/part_export.cgi +++ b/httemplate/edit/part_export.cgi @@ -46,6 +46,8 @@ my $widget = new HTML::Widgets::SelectLayers( foreach my $option ( keys %{$exports->{$layer}{options}} ) { my $optinfo = $exports->{$layer}{options}{$option}; + die "Retreived non-ref export info option from $layer export: $optinfo" + unless ref($optinfo); my $label = $optinfo->{label}; my $type = defined($optinfo->{type}) ? $optinfo->{type} : 'text'; my $value = $cgi->param($option) diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi index 7f7b2b0d4..9271222ad 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -152,9 +152,11 @@ print ' CHECKED' if $hashref->{disabled} eq "Y"; print '>'; print '</TD></TR></TABLE>'; -my $thead = "\n\n". ntable('#cccccc', 2). <<END; -<TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH><TH BGCOLOR="#dcdcdc">Service</TH></TR> -END +my $thead = "\n\n". ntable('#cccccc', 2). + '<TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH>'; +$thead .= '<TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Primary</FONT></TH>' + if dbdef->table('pkg_svc')->column('primary_svc'); +$thead .= '<TH BGCOLOR="#dcdcdc">Service</TH></TR>'; #unless ( $cgi->param('clone') ) { #dunno why... @@ -176,9 +178,10 @@ foreach my $part_svc ( @part_svc ) { 'pkgpart' => $pkgpart, 'svcpart' => $svcpart, } ) || new FS::pkg_svc ( { - 'pkgpart' => $pkgpart, - 'svcpart' => $svcpart, - 'quantity' => 0, + 'pkgpart' => $pkgpart, + 'svcpart' => $svcpart, + 'quantity' => 0, + 'primary_svc' => '', }); #? #next unless $pkg_svc; @@ -190,7 +193,13 @@ foreach my $part_svc ( @part_svc ) { print '<TR>'; # if $count == 0 ; print qq!<TD><INPUT TYPE="text" NAME="pkg_svc$svcpart" SIZE=4 MAXLENGTH=3 VALUE="!, $cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0, - qq!"></TD><TD><A HREF="part_svc.cgi?!,$part_svc->svcpart, + qq!"></TD>!; + if ( dbdef->table('pkg_svc')->column('primary_svc') ) { + print qq!<TD><INPUT TYPE="radio" NAME="pkg_svc_primary" VALUE="$svcpart"!; + print ' CHECKED' if $pkg_svc->primary_svc =~ /^Y/i; + print '></TD>'; + } + print qq!<TD><A HREF="part_svc.cgi?!,$part_svc->svcpart, qq!">!, $part_svc->getfield('svc'), "</A></TD></TR>"; # print "</TABLE></TD><TD>$thead" if ++$count == int(scalar(@part_svc) / 2); $count+=1; @@ -488,6 +497,10 @@ if ( $conf->exists('enable_taxclasses') ) { push @fixups, 'taxclass'; #hidden } +my @form_radio = (); +if ( dbdef->table('pkg_svc')->column('primary_svc') ) { + push @form_radio, 'pkg_svc_primary'; +} my $widget = new HTML::Widgets::SelectLayers( 'selected_layer' => $part_pkg->plan, @@ -496,7 +509,8 @@ my $widget = new HTML::Widgets::SelectLayers( 'form_action' => 'process/part_pkg.cgi', 'form_text' => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ], 'form_checkbox' => [ qw(setuptax recurtax disabled) ], - 'form_select' => [ @form_select ], + 'form_radio' => \@form_radio, + 'form_select' => \@form_select, 'fixup_callback' => sub { #my $ = @_; my $html = ''; diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi index 683bf9ec5..21bb3ad4f 100755 --- a/httemplate/edit/part_svc.cgi +++ b/httemplate/edit/part_svc.cgi @@ -1,33 +1,74 @@ -<!-- mason kludge --> -<% - my $part_svc; - my $clone = ''; - if ( $cgi->param('error') ) { #error - $part_svc = new FS::part_svc ( { - map { $_, scalar($cgi->param($_)) } fields('part_svc') - } ); - } elsif ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone - #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query"; - $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } ) - or die "unknown svcpart: $1"; - $clone = $part_svc->svcpart; - $part_svc->svcpart(''); - } elsif ( $cgi->keywords ) { #edit - my($query) = $cgi->keywords; - $query =~ /^(\d+)$/ or die "malformed query: $query"; - $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } ) - or die "unknown svcpart: $1"; - } else { #adding - $part_svc = new FS::part_svc {}; - } - my $action = $part_svc->svcpart ? 'Edit' : 'Add'; - my $hashref = $part_svc->hashref; +<% +my $part_svc; +my $clone = ''; +my $error = ''; +if ( $cgi->param('magic') eq 'process' ) { + + my $svcpart = $cgi->param('svcpart'); + my $old = qsearchs('part_svc', { 'svcpart' => $svcpart }) if $svcpart; + + $cgi->param( 'svc_acct__usergroup', + join(',', $cgi->param('svc_acct__usergroup') ) ); + + my $new = new FS::part_svc ( { + map { + $_, scalar($cgi->param($_)); + # } qw(svcpart svc svcdb) + } ( fields('part_svc'), + map { my $svcdb = $_; + my @fields = fields($svcdb); + push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge + map { ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' ) } @fields; + } grep defined( $FS::Record::dbdef->table($_) ), + qw( svc_acct svc_domain svc_forward svc_www svc_broadband ) + ) + } ); + + my %exportnums = + map { $_->exportnum => ( $cgi->param('exportnum'.$_->exportnum) || '') } + qsearch('part_export', {} ); + + if ( $svcpart ) { + $error = $new->replace($old, '1.3-COMPAT', [ 'usergroup' ], \%exportnums ); + } else { + $error = $new->insert( [ 'usergroup' ], \%exportnums ); + $svcpart = $new->getfield('svcpart'); + } + + unless ( $error ) { #no error, redirect + #print $cgi->redirect(popurl(3)."browse/part_svc.cgi"); + print $cgi->redirect("${p}browse/part_svc.cgi"); + myexit; + } + + $part_svc = $new; #?? + #$part_svc = new FS::part_svc ( { + # map { $_, scalar($cgi->param($_)) } fields('part_svc') + #} ); + +} elsif ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone + #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query"; + $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } ) + or die "unknown svcpart: $1"; + $clone = $part_svc->svcpart; + $part_svc->svcpart(''); +} elsif ( $cgi->keywords ) { #edit + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "malformed query: $query"; + $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } ) + or die "unknown svcpart: $1"; +} else { #adding + $part_svc = new FS::part_svc {}; +} + +my $action = $part_svc->svcpart ? 'Edit' : 'Add'; +my $hashref = $part_svc->hashref; # my $p_svcdb = $part_svc->svcdb || 'svc_acct'; #" onLoad=\"visualize()\"" %> - +<!-- mason kludge --> <%= header("$action Service Definition", menubar( 'Main Menu' => $p, 'View all service definitions' => "${p}browse/part_svc.cgi" @@ -35,8 +76,8 @@ ) %> -<% if ( $cgi->param('error') ) { %> -<FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT> +<% if ( $error ) { %> +<FONT SIZE="+1" COLOR="#ff0000">Error: <%= $error %></FONT> <% } %> <FORM NAME="dummy"> @@ -45,6 +86,7 @@ <BR><BR> Service <INPUT TYPE="text" NAME="svc" VALUE="<%= $hashref->{svc} %>"><BR> Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR> +<INPUT TYPE="hidden" NAME="magic" VALUE="process"> <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $hashref->{svcpart} %>"> <BR> Services are items you offer to your customers. @@ -67,6 +109,7 @@ blank <B>slipip</B> as well as a fixed shell something like <B>/bin/true</B> or <% #these might belong somewhere else for other user interfaces #pry need to eventually create stuff that's shared amount UIs +my $conf = new FS::Conf; my %defs = ( 'svc_acct' => { 'dir' => 'Home directory', @@ -87,7 +130,11 @@ my %defs = ( 'quota' => '', '_password' => 'Password', 'gid' => 'GID (when blank, defaults to UID)', - 'shell' => 'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file)', + 'shell' => { + desc =>'Shell (all service definitions should have a default or fixed shell that is present in the <b>shells</b> configuration file)', + type =>'select', + select_list => [ $conf->config('shells') ], + }, 'finger' => 'GECOS', 'domsvc' => { desc =>'svcnum from svc_domain', @@ -137,8 +184,9 @@ my %defs = ( 'selected_layer' => $hashref->{svcdb} || 'svc_acct', 'options' => \%svcdb, 'form_name' => 'dummy', - 'form_action' => 'process/part_svc.cgi', - 'form_text' => [ qw( svc svcpart ) ], + #'form_action' => 'process/part_svc.cgi', + 'form_action' => 'part_svc.cgi', #self + 'form_text' => [ qw( magic svc svcpart ) ], 'form_checkbox' => [ 'disabled' ], 'layer_callback' => sub { my $layer = shift; @@ -175,10 +223,10 @@ my %defs = ( $part_svc->svcpart($clone) if $clone; #haha, undone below foreach my $field (@fields) { my $part_svc_column = $part_svc->part_svc_column($field); - my $value = $cgi->param('error') + my $value = $error ? $cgi->param("${layer}__${field}") : $part_svc_column->columnvalue; - my $flag = $cgi->param('error') + my $flag = $error ? $cgi->param("${layer}__${field}_flag") : $part_svc_column->columnflag; my $def = $defs{$layer}{$field}; @@ -204,12 +252,20 @@ my %defs = ( if ( $def->{type} eq 'select' ) { $html .= qq!<SELECT NAME="${layer}__${field}">!; $html .= '<OPTION> </OPTION>' unless $value; - foreach my $record ( qsearch( $def->{select_table}, {} ) ) { - my $rvalue = $record->getfield($def->{select_key}); - $html .= qq!<OPTION VALUE="$rvalue"!. - ( $rvalue==$value ? ' SELECTED>' : '>' ). - $record->getfield($def->{select_label}). '</OPTION>'; - } + if ( $def->{select_table} ) { + foreach my $record ( qsearch( $def->{select_table}, {} ) ) { + my $rvalue = $record->getfield($def->{select_key}); + $html .= qq!<OPTION VALUE="$rvalue"!. + ( $rvalue==$value ? ' SELECTED>' : '>' ). + $record->getfield($def->{select_label}). '</OPTION>'; + } #next $record + } else { # select_list + foreach my $item ( @{$def->{select_list}} ) { + $html .= qq!<OPTION VALUE="$item"!. + ( $item eq $value ? ' SELECTED>' : '>' ). + $item. '</OPTION>'; + } #next $item + } #endif $html .= '</SELECT>'; } elsif ( $def->{type} eq 'radius_usergroup_selector' ) { $html .= FS::svc_acct::radius_usergroup_selector( diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi index 7f5c5e49c..3d697ddfd 100755 --- a/httemplate/edit/process/REAL_cust_pkg.cgi +++ b/httemplate/edit/process/REAL_cust_pkg.cgi @@ -16,7 +16,9 @@ if ( $error ) { $cgi->param('error', $error); print $cgi->redirect(popurl(2). "REAL_cust_pkg.cgi?". $cgi->query_string ); } else { - print $cgi->redirect(popurl(3). "view/cust_pkg.cgi?". $pkgnum); + my $custnum = $new->custnum; + print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum". + "#cust_pkg$pkgnum" ); } %> diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 718f0e501..cb95ebed5 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -23,8 +23,6 @@ if ( $payby ) { if defined $cgi->param( $payby. '_paycvv' ); } -$cgi->param('otaker', &getotaker ); - my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') ); push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST'); $cgi->param('invoicing_list', join(',', @invoicing_list) ); diff --git a/httemplate/edit/process/cust_main_county-collapse.cgi b/httemplate/edit/process/cust_main_county-collapse.cgi index 8e67140a8..5da9dea80 100755 --- a/httemplate/edit/process/cust_main_county-collapse.cgi +++ b/httemplate/edit/process/cust_main_county-collapse.cgi @@ -3,8 +3,8 @@ my($query) = $cgi->keywords; $query =~ /^(\d+)$/ or die "Illegal taxnum!"; my $taxnum = $1; -my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum}) - or die ("Unknown taxnum!"); +my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) + or die "Unknown taxnum $taxnum"; #really should do this in a .pm & start transaction diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi index 82442ae00..87d6011e7 100755 --- a/httemplate/edit/process/cust_pay.cgi +++ b/httemplate/edit/process/cust_pay.cgi @@ -8,11 +8,14 @@ $cgi->param('link') =~ /^(custnum|invnum)$/ or die "Illegal link: ". $cgi->param('link'); my $link = $1; +my $_date = str2time($cgi->param('_date')); + my $new = new FS::cust_pay ( { $link => $linknum, + _date => $_date, map { $_, scalar($cgi->param($_)); - } qw(paid _date payby payinfo paybatch) + } qw(paid payby payinfo paybatch) #} fields('cust_pay') } ); diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi index d489426f9..5ff3e6f17 100755 --- a/httemplate/edit/process/part_pkg.cgi +++ b/httemplate/edit/process/part_pkg.cgi @@ -23,87 +23,31 @@ my $new = new FS::part_pkg ( { } fields('part_pkg') } ); -#warn "setuptax: ". $new->setuptax; -#warn "recurtax: ". $new->recurtax; - -#most of the stuff below should move to part_pkg.pm - -foreach my $part_svc ( qsearch('part_svc', {} ) ) { - my $quantity = $cgi->param('pkg_svc'. $part_svc->svcpart) || 0; - unless ( $quantity =~ /^(\d+)$/ ) { - $cgi->param('error', "Illegal quantity" ); - print $cgi->redirect(popurl(2). "part_pkg.cgi?". $cgi->query_string ); - myexit(); - } -} - -local $SIG{HUP} = 'IGNORE'; -local $SIG{INT} = 'IGNORE'; -local $SIG{QUIT} = 'IGNORE'; -local $SIG{TERM} = 'IGNORE'; -local $SIG{TSTP} = 'IGNORE'; -local $SIG{PIPE} = 'IGNORE'; - -local $FS::UID::AutoCommit = 0; +my %pkg_svc = map { $_ => $cgi->param("pkg_svc$_") } + map { $_->svcpart } + qsearch('part_svc', {} ); my $error; +my $custnum = ''; if ( $pkgpart ) { - $error = $new->replace($old); + $error = $new->replace( $old, 'pkg_svc' => \%pkg_svc, + 'primary_svc' => $cgi->param('pkg_svc_primary'), + ); } else { - $error = $new->insert; - $pkgpart=$new->pkgpart; + $error = $new->insert( 'pkg_svc' => \%pkg_svc, + 'primary_svc' => $cgi->param('pkg_svc_primary'), + 'cust_pkg' => $cgi->param('pkgnum'), + 'custnum_ref' => \$custnum, + ); + $pkgpart = $new->pkgpart; } if ( $error ) { - $dbh->rollback; $cgi->param('error', $error ); print $cgi->redirect(popurl(2). "part_pkg.cgi?". $cgi->query_string ); - myexit(); -} - -foreach my $part_svc (qsearch('part_svc',{})) { - my $quantity = $cgi->param('pkg_svc'. $part_svc->svcpart) || 0; - my $old_pkg_svc = qsearchs('pkg_svc', { - 'pkgpart' => $pkgpart, - 'svcpart' => $part_svc->svcpart, - } ); - my $old_quantity = $old_pkg_svc ? $old_pkg_svc->quantity : 0; - next unless $old_quantity != $quantity; #!here - my $new_pkg_svc = new FS::pkg_svc( { - 'pkgpart' => $pkgpart, - 'svcpart' => $part_svc->svcpart, - 'quantity' => $quantity, - } ); - if ( $old_pkg_svc ) { - my $myerror = $new_pkg_svc->replace($old_pkg_svc); - if ( $myerror ) { - $dbh->rollback; - die $myerror; - } - } else { - my $myerror = $new_pkg_svc->insert; - if ( $myerror ) { - $dbh->rollback; - die $myerror; - } - } -} - -unless ( $cgi->param('pkgnum') && $cgi->param('pkgnum') =~ /^(\d+)$/ ) { - $dbh->commit or die $dbh->errstr; - print $cgi->redirect(popurl(3). "browse/part_pkg.cgi"); +} elsif ( $custnum ) { + print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum"); } else { - my($old_cust_pkg) = qsearchs( 'cust_pkg', { 'pkgnum' => $1 } ); - my %hash = $old_cust_pkg->hash; - $hash{'pkgpart'} = $pkgpart; - my($new_cust_pkg) = new FS::cust_pkg \%hash; - my $myerror = $new_cust_pkg->replace($old_cust_pkg); - if ( $myerror ) { - $dbh->rollback; - die "Error modifying cust_pkg record: $myerror\n"; - } - - $dbh->commit or die $dbh->errstr; - print $cgi->redirect(popurl(3). "view/cust_main.cgi?". $new_cust_pkg->custnum); + print $cgi->redirect(popurl(3). "browse/part_pkg.cgi"); } %> diff --git a/httemplate/edit/process/part_svc.cgi b/httemplate/edit/process/part_svc.cgi deleted file mode 100755 index 859670b17..000000000 --- a/httemplate/edit/process/part_svc.cgi +++ /dev/null @@ -1,62 +0,0 @@ -<% - -my $svcpart = $cgi->param('svcpart'); - -my $old = qsearchs('part_svc',{'svcpart'=>$svcpart}) if $svcpart; - -$cgi->param( 'svc_acct__usergroup', - join(',', $cgi->param('svc_acct__usergroup') ) ); - -my $new = new FS::part_svc ( { - map { - $_, scalar($cgi->param($_)); -# } qw(svcpart svc svcdb) - } ( fields('part_svc'), - map { my $svcdb = $_; - my @fields = fields($svcdb); - push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge - map { ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' ) } @fields; - } grep defined( $FS::Record::dbdef->table($_) ), - qw( svc_acct svc_domain svc_acct_sm svc_forward svc_www ) - ) -} ); - -my $error; -if ( $svcpart ) { - $error = $new->replace($old, '1.3-COMPAT', [ 'usergroup' ] ); -} else { - $error = $new->insert( [ 'usergroup' ] ); - $svcpart=$new->getfield('svcpart'); -} - -if ( $error ) { - $cgi->param('error', $error); - print $cgi->redirect(popurl(2). "part_svc.cgi?". $cgi->query_string ); -} else { - - #false laziness w/ edit/process/agent_type.cgi - foreach my $part_export (qsearch('part_export',{})) { - my $exportnum = $part_export->exportnum; - my $export_svc = qsearchs('export_svc', { - 'exportnum' => $part_export->exportnum, - 'svcpart' => $new->svcpart, - } ); - if ( $export_svc && ! $cgi->param("exportnum". $part_export->exportnum) ) { - $error = $export_svc->delete; - die $error if $error; - } elsif ( $cgi->param("exportnum". $part_export->exportnum) - && ! $export_svc ) { - $export_svc = new FS::export_svc ( { - 'exportnum' => $part_export->exportnum, - 'svcpart' => $new->svcpart, - } ); - $error = $export_svc->insert; - die $error if $error; - } - - } - - print $cgi->redirect(popurl(3)."browse/part_svc.cgi"); -} - -%> diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index a8f5b1453..fd9e59472 100644 --- a/httemplate/edit/process/quick-cust_pkg.cgi +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -17,7 +17,8 @@ if ($error) { <% eidiot($error); } else { - print $cgi->redirect(popurl(3). "view/cust_pkg.cgi?". $cust_pkg[0]->pkgnum ); + print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum". + "#cust_pkg". $cust_pkg[0]->pkgnum ); } %> diff --git a/httemplate/edit/svc_forward.cgi b/httemplate/edit/svc_forward.cgi index bc19fe1de..6ac6a928b 100755 --- a/httemplate/edit/svc_forward.cgi +++ b/httemplate/edit/svc_forward.cgi @@ -59,20 +59,17 @@ if ( $cgi->param('error') ) { my $action = $svc_forward->svcnum ? 'Edit' : 'Add'; my %email; + +#starting with those currently attached +foreach my $method (qw( srcsvc_acct dstsvc_acct )) { + my $svc_acct = $svc_forward->$method(); + $email{$svc_acct->svcnum} = $svc_acct->email if $svc_acct; +} + if ($pkgnum) { #find all possible user svcnums (and emails) - #starting with those currently attached - if ( $svc_forward->srcsvc ) { - my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_forward->srcsvc } ); - $email{$svc_forward->srcsvc} = $svc_acct->email; - } - if ( $svc_forward->dstsvc ) { - my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_forward->dstsvc } ); - $email{$svc_forward->dstsvc} = $svc_acct->email; - } - #and including the rest for this customer my($u_part_svc,@u_acct_svcparts); foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) { @@ -99,15 +96,7 @@ if ($pkgnum) { } } -} elsif ( $action eq 'Edit' ) { - - my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$svc_forward->srcsvc}); - $email{$svc_forward->srcsvc} = $svc_acct->email; - - $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svc_forward->dstsvc}); - $email{$svc_forward->dstsvc} = $svc_acct->email; - -} else { +} elsif ( $action eq 'Add' ) { die "\$action eq Add, but \$pkgnum is null!\n"; } @@ -116,6 +105,7 @@ my($srcsvc,$dstsvc,$dst)=( $svc_forward->dstsvc, $svc_forward->dst, ); +my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : ''; #display @@ -131,46 +121,58 @@ my($srcsvc,$dstsvc,$dst)=( Service #<%= $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR> Service: <B><%= $part_svc->svc %></B><BR><BR> -<FORM NAME="dummy"> +<FORM ACTION="process/svc_forward.cgi" METHOD="POST"> +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%= $svcnum %>"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>"> + +<SCRIPT TYPE="text/javascript"> +function srcchanged(what) { + if ( what.options[what.selectedIndex].value == 0 ) { + what.form.src.disabled = false; + what.form.src.style.backgroundColor = "white"; + } else { + what.form.src.disabled = true; + what.form.src.style.backgroundColor = "lightgrey"; + } +} +function dstchanged(what) { + if ( what.options[what.selectedIndex].value == 0 ) { + what.form.dst.disabled = false; + what.form.dst.style.backgroundColor = "white"; + } else { + what.form.dst.disabled = true; + what.form.dst.style.backgroundColor = "lightgrey"; + } +} +</SCRIPT> <%= ntable("#cccccc",2) %> -<TR><TD ALIGN="right">Email to</TD><TD><SELECT NAME="srcsvc" SIZE=1> +<TR><TD ALIGN="right">Email to</TD> +<TD><SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)"> <% foreach $_ (keys %email) { %> <OPTION<%= $_ eq $srcsvc ? " SELECTED" : "" %> VALUE="<%= $_ %>"><%= $email{$_} %></OPTION> <% } %> -</SELECT></TD></TR> - -<% - tie my %tied_email, 'Tie::IxHash', - '' => 'SELECT DESTINATION', - %email, - '0' => '(other email address)'; - my $widget = new HTML::Widgets::SelectLayers( - 'selected_layer' => $dstsvc, - 'options' => \%tied_email, - 'form_name' => 'dummy', - 'form_action' => 'process/svc_forward.cgi', - 'form_select' => ['srcsvc'], - 'html_between' => '</TD></TR></TABLE>', - 'layer_callback' => sub { - my $layer = shift; - my $html = qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!. - qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!. - qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!. - qq!<INPUT TYPE="hidden" NAME="dstsvc" VALUE="$layer">!; - if ( $layer eq '0' ) { - $html .= ntable("#cccccc",2). - '<TR><TD ALIGN="right">Destination email</TD>'. - qq!<TD><INPUT TYPE="text" NAME="dst" VALUE="$dst"></TD>!. - '</TR></TABLE>'; - } - $html .= '<BR><INPUT TYPE="submit" VALUE="Submit">'; - $html; - }, - ); -%> +<% if ( $svc_forward->dbdef_table->column('src') ) { %> + <OPTION <%= $src ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION> +<% } %> +</SELECT> +<% if ( $svc_forward->dbdef_table->column('src') ) { %> +<INPUT TYPE="text" NAME="src" VALUE="<%= $src %>" <%= ( $src || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>> +<% } %> +</TD></TR> <TR><TD ALIGN="right">Forwards to</TD> -<TD><%= $widget->html %> +<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)"> +<% foreach $_ (keys %email) { %> + <OPTION<%= $_ eq $dstsvc ? " SELECTED" : "" %> VALUE="<%= $_ %>"><%= $email{$_} %></OPTION> +<% } %> +<OPTION <%= $dst ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION> +</SELECT> +<INPUT TYPE="text" NAME="dst" VALUE="<%= $dst %>" <%= ( $dst || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>> +</TD></TR> + </TABLE> +<BR><INPUT TYPE="submit" VALUE="Submit"> +</FORM> </BODY> </HTML> diff --git a/httemplate/edit/svc_www.cgi b/httemplate/edit/svc_www.cgi index d2c9ade5c..e15978fde 100644 --- a/httemplate/edit/svc_www.cgi +++ b/httemplate/edit/svc_www.cgi @@ -1,6 +1,8 @@ <!-- mason kludge --> <% +my $conf = new FS::Conf; + my( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_www ); if ( $cgi->param('error') ) { $svc_www = new FS::svc_www ( { @@ -53,12 +55,17 @@ if ( $cgi->param('error') ) { } my $action = $svc_www->svcnum ? 'Edit' : 'Add'; -my( %username, %arec ); +my( %svc_acct, %arec ); if ($pkgnum) { - my($u_part_svc,@u_acct_svcparts); - foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) { - push @u_acct_svcparts,$u_part_svc->getfield('svcpart'); + my @u_acct_svcparts; + foreach my $svcpart ( + map { $_->svcpart } qsearch( 'part_svc', { 'svcdb' => 'svc_acct' } ) + ) { + next if $conf->exists('svc_www-usersvc_svcpart') + && ! grep { $svcpart == $_ } + $conf->config('svc_www-usersvc_svcpart'); + push @u_acct_svcparts, $svcpart; } my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); @@ -73,7 +80,8 @@ if ($pkgnum) { my($i_cust_svc); foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); - $username{$svc_acct->getfield('svcnum')}=$svc_acct->getfield('username'); + $svc_acct{$svc_acct->getfield('svcnum')}= + $svc_acct->cust_svc->part_svc->svc. ': '. $svc_acct->email; } } } @@ -84,33 +92,60 @@ if ($pkgnum) { push @d_acct_svcparts,$d_part_svc->getfield('svcpart'); } - foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { - my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); - my($acct_svcpart); - foreach $acct_svcpart (@d_acct_svcparts) { - my($i_cust_svc); - foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { - my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); - my $domain_rec; - foreach $domain_rec ( qsearch('domain_record',{ - 'svcnum' => $svc_domain->svcnum, - 'rectype' => 'A' } ), - qsearch('domain_record',{ - 'svcnum' => $svc_domain->svcnum, - 'rectype' => 'CNAME' - } ) ) { - $arec{$domain_rec->recnum} = - $domain_rec->reczone eq '@' - ? $svc_domain->domain - : $domain_rec->reczone. '.'. $svc_domain->domain; + foreach $i_cust_pkg ( qsearch( 'cust_pkg', { 'custnum' => $custnum } ) ) { + my $cust_pkgnum = $i_cust_pkg->pkgnum; + + foreach my $acct_svcpart (@d_acct_svcparts) { + + foreach my $i_cust_svc ( + qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum, + 'svcpart' => $acct_svcpart } ) + ) { + my $svc_domain = + qsearchs( 'svc_domain', { 'svcnum' => $i_cust_svc->svcnum } ); + + my $extra_sql = "AND ( rectype = 'A' OR rectype = 'CNAME' )"; + unless ( $conf->exists('svc_www-enable_subdomains') ) { + $extra_sql .= " AND ( reczone = '\@' OR reczone = '". + $svc_domain->domain. ".' )"; + } + + foreach my $domain_rec ( + qsearch( 'domain_record', + { + 'svcnum' => $svc_domain->svcnum, + }, + '', + $extra_sql, + ) + ) { + $arec{$domain_rec->recnum} = $domain_rec->zone; + } + + if ( $conf->exists('svc_www-enable_subdomains') ) { + $arec{'www.'. $svc_domain->domain} = 'www.'. $svc_domain->domain + unless qsearchs( 'domain_record', { + svcnum => $svc_domain->svcnum, + reczone => 'www', + } ) + || qsearchs( 'domain_record', { + svcnum => $svc_domain->svcnum, + reczone => 'www.'.$svc-domain->domain.'.', + } ); } + $arec{'@.'. $svc_domain->domain} = $svc_domain->domain - unless qsearchs('domain_record', { svcnum => $svc_domain->svcnum, - reczone => '@', } ); - $arec{'www.'. $svc_domain->domain} = 'www.'. $svc_domain->domain - unless qsearchs('domain_record', { svcnum => $svc_domain->svcnum, - reczone => 'www', } ); + unless qsearchs('domain_record', { + svcnum => $svc_domain->svcnum, + reczone => '@', + } ) + || qsearchs('domain_record', { + svcnum => $svc_domain->svcnum, + reczone => $svc_domain->domain.'.', + } ); + } + } } @@ -161,9 +196,9 @@ foreach $_ (keys %arec) { print "</SELECT></TD></TR>"; print '<TR><TD ALIGN="right">Username</TD><TD><SELECT NAME="usersvc" SIZE=1>'; -foreach $_ (keys %username) { +foreach $_ (keys %svc_acct) { print "<OPTION", ($_ eq $usersvc) ? " SELECTED" : "", - qq! VALUE="$_">$username{$_}!; + qq! VALUE="$_">$svc_acct{$_}!; } print "</SELECT></TD></TR>"; diff --git a/httemplate/elements/calendar-en.js b/httemplate/elements/calendar-en.js index e9291e1b7..e9d6a222e 100644 --- a/httemplate/elements/calendar-en.js +++ b/httemplate/elements/calendar-en.js @@ -101,8 +101,16 @@ Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)"; Calendar._TT["SEL_DATE"] = "Select date"; Calendar._TT["DRAG_TO_MOVE"] = "Drag to move"; Calendar._TT["PART_TODAY"] = " (today)"; -Calendar._TT["MON_FIRST"] = "Display Monday first"; -Calendar._TT["SUN_FIRST"] = "Display Sunday first"; + +// the following is to inform that "%s" is to be the first day of week +// %s will be replaced with the day name. +Calendar._TT["DAY_FIRST"] = "Display %s first"; + +// This may be locale-dependent. It specifies the week-end days, as an array +// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1 +// means Monday, etc. +Calendar._TT["WEEKEND"] = "0,6"; + Calendar._TT["CLOSE"] = "Close"; Calendar._TT["TODAY"] = "Today"; Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value"; @@ -112,3 +120,4 @@ Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d"; Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e"; Calendar._TT["WK"] = "wk"; +Calendar._TT["TIME"] = "Time:"; diff --git a/httemplate/elements/calendar-setup.js b/httemplate/elements/calendar-setup.js index 6f1d7a232..e9ee7ea87 100644 --- a/httemplate/elements/calendar-setup.js +++ b/httemplate/elements/calendar-setup.js @@ -19,7 +19,7 @@ * than modifying calendar.js itself). */ -// $Id: calendar-setup.js,v 1.1.2.2 2003-11-07 10:53:36 ivan Exp $ +// $Id: calendar-setup.js,v 1.1.2.3 2004-09-22 11:04:48 ivan Exp $ /** * This function "patches" an input field (or other element) to use a calendar @@ -36,8 +36,8 @@ * ifFormat | date format that will be stored in the input field * daFormat | the date format that will be used to display the date in displayArea * singleClick | (true/false) wether the calendar is in single click mode or not (default: true) - * mondayFirst | (true/false) if true Monday is the first day of week, Sunday otherwise (default: true) - * align | alignment (default: "Bl"); if you don't know what's this see the calendar documentation + * firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc. + * align | alignment (default: "Br"); if you don't know what's this see the calendar documentation * range | array with 2 elements. Default: [1900, 2999] -- the range of years available * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID @@ -49,6 +49,11 @@ * date | the date that the calendar will be initially displayed to * showsTime | default: false; if true the calendar will include a time selector * timeFormat | the time format; can be "12" or "24", default is "12" + * electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close + * step | configures the step of the years in drop-down boxes; default: 2 + * position | configures the calendar absolute position; default: null + * cache | if "true" (but default: "false") it will reuse the same calendar object, where possible + * showOthers | if "true" (but default: "false") it will show days from other months too * * None of them is required, they all have default values. However, if you * pass none of "inputField", "displayArea" or "button" you'll get a warning @@ -66,8 +71,8 @@ Calendar.setup = function (params) { param_default("singleClick", true); param_default("disableFunc", null); param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined - param_default("mondayFirst", true); - param_default("align", "Bl"); + param_default("firstDay", 0); // defaults to "Sunday" first + param_default("align", "Br"); param_default("range", [1900, 2999]); param_default("weekNumbers", true); param_default("flat", null); @@ -78,6 +83,11 @@ Calendar.setup = function (params) { param_default("date", null); param_default("showsTime", false); param_default("timeFormat", "24"); + param_default("electric", true); + param_default("step", 2); + param_default("position", null); + param_default("cache", false); + param_default("showOthers", false); var tmp = ["inputField", "displayArea", "button"]; for (var i in tmp) { @@ -91,35 +101,36 @@ Calendar.setup = function (params) { } function onSelect(cal) { - if (cal.params.flat) { - if (typeof cal.params.flatCallback == "function") { - cal.params.flatCallback(cal); - } else { + var p = cal.params; + var update = (cal.dateClicked || p.electric); + if (update && p.flat) { + if (typeof p.flatCallback == "function") + p.flatCallback(cal); + else alert("No flatCallback given -- doing nothing."); - } return false; } - if (cal.params.inputField) { - cal.params.inputField.value = cal.date.print(cal.params.ifFormat); + if (update && p.inputField) { + p.inputField.value = cal.date.print(p.ifFormat); + if (typeof p.inputField.onchange == "function") + p.inputField.onchange(); } - if (cal.params.displayArea) { - cal.params.displayArea.innerHTML = cal.date.print(cal.params.daFormat); - } - if (cal.params.singleClick && cal.dateClicked) { + if (update && p.displayArea) + p.displayArea.innerHTML = cal.date.print(p.daFormat); + if (update && p.singleClick && cal.dateClicked) cal.callCloseHandler(); - } - if (typeof cal.params.onUpdate == "function") { - cal.params.onUpdate(cal); - } + if (update && typeof p.onUpdate == "function") + p.onUpdate(cal); }; if (params.flat != null) { - params.flat = document.getElementById(params.flat); + if (typeof params.flat == "string") + params.flat = document.getElementById(params.flat); if (!params.flat) { alert("Calendar.setup:\n Flat specified but can't find parent."); return false; } - var cal = new Calendar(params.mondayFirst, params.date, params.onSelect || onSelect); + var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect); cal.showsTime = params.showsTime; cal.time24 = (params.timeFormat == "24"); cal.params = params; @@ -137,8 +148,8 @@ Calendar.setup = function (params) { var dateFmt = params.inputField ? params.ifFormat : params.daFormat; var mustCreate = false; var cal = window.calendar; - if (!window.calendar) { - window.calendar = cal = new Calendar(params.mondayFirst, + if (!(cal && params.cache)) { + window.calendar = cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect, params.onClose || function(cal) { cal.hide(); }); @@ -147,8 +158,12 @@ Calendar.setup = function (params) { cal.weekNumbers = params.weekNumbers; mustCreate = true; } else { + if (params.date) + cal.setDate(params.date); cal.hide(); } + cal.showsOtherMonths = params.showOthers; + cal.yearStep = params.step; cal.setRange(params.range[0], params.range[1]); cal.params = params; cal.setDateStatusHandler(params.dateStatusFunc); @@ -157,7 +172,10 @@ Calendar.setup = function (params) { cal.create(); cal.parseDate(dateEl.value || dateEl.innerHTML); cal.refresh(); - cal.showAtElement(params.displayArea || params.inputField, params.align); + if (!params.position) + cal.showAtElement(params.button || params.displayArea || params.inputField, params.align); + else + cal.showAt(params.position[0], params.position[1]); return false; }; }; diff --git a/httemplate/elements/calendar-win2k-2.css b/httemplate/elements/calendar-win2k-2.css index 9727d1b9a..6001cfaa4 100644 --- a/httemplate/elements/calendar-win2k-2.css +++ b/httemplate/elements/calendar-win2k-2.css @@ -92,6 +92,13 @@ text-align: right; padding: 2px 4px 2px 2px; } +.calendar tbody .day.othermonth { + font-size: 80%; + color: #aaa; +} +.calendar tbody .day.othermonth.oweekend { + color: #faa; +} .calendar table .wn { padding: 2px 3px 2px 2px; @@ -185,7 +192,7 @@ /* Combo boxes (menus that display months/years for direct selection) */ -.combo { +.calendar .combo { position: absolute; display: none; width: 4em; @@ -197,21 +204,21 @@ border-bottom: 1px solid #000; border-left: 1px solid #fff; background: #e4d8e0; - font-size: smaller; + font-size: 90%; padding: 1px; } -.combo .label, -.combo .label-IEfix { +.calendar .combo .label, +.calendar .combo .label-IEfix { text-align: center; padding: 1px; } -.combo .label-IEfix { +.calendar .combo .label-IEfix { width: 4em; } -.combo .active { +.calendar .combo .active { background: #d4c8d0; padding: 0px; border-top: 1px solid #000; @@ -220,7 +227,7 @@ border-left: 1px solid #000; } -.combo .hilite { +.calendar .combo .hilite { background: #408; color: #fea; } diff --git a/httemplate/elements/calendar.js b/httemplate/elements/calendar.js index 9503f3957..0c88b332d 100644 --- a/httemplate/elements/calendar.js +++ b/httemplate/elements/calendar.js @@ -1,7 +1,7 @@ /* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/ * ------------------------------------------------------------------ * - * The DHTML Calendar, version 0.9.5 "Your favorite time, bis" + * The DHTML Calendar, version 0.9.6 "Keep cool but don't freeze" * * Details and latest version at: * http://dynarch.com/mishoo/calendar.epl @@ -10,10 +10,10 @@ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html */ -// $Id: calendar.js,v 1.1.2.2 2003-11-07 10:53:36 ivan Exp $ +// $Id: calendar.js,v 1.1.2.3 2004-09-22 11:04:48 ivan Exp $ /** The Calendar object constructor. */ -Calendar = function (mondayFirst, dateStr, onSelected, onClose) { +Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) { // member variables this.activeDiv = null; this.currentDateEl = null; @@ -29,11 +29,13 @@ Calendar = function (mondayFirst, dateStr, onSelected, onClose) { this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"]; this.isPopup = true; this.weekNumbers = true; - this.mondayFirst = mondayFirst; + this.firstDayOfWeek = firstDayOfWeek; // 0 for Sunday, 1 for Monday, etc. + this.showsOtherMonths = false; this.dateStr = dateStr; this.ar_days = null; this.showsTime = false; this.time24 = true; + this.yearStep = 2; // HTML elements this.table = null; this.element = null; @@ -79,6 +81,8 @@ Calendar._C = null; Calendar.is_ie = ( /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) ); +Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) ); + /// detect Opera browser Calendar.is_opera = /opera/i.test(navigator.userAgent); @@ -97,7 +101,7 @@ Calendar.getAbsolutePos = function(el) { ST = el.scrollTop; var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST }; if (el.offsetParent) { - var tmp = Calendar.getAbsolutePos(el.offsetParent); + var tmp = this.getAbsolutePos(el.offsetParent); r.x += tmp.x; r.y += tmp.y; } @@ -261,8 +265,13 @@ Calendar.showMonthsCombo = function () { s.display = "block"; if (cd.navtype < 0) s.left = cd.offsetLeft + "px"; - else - s.left = (cd.offsetLeft + cd.offsetWidth - mc.offsetWidth) + "px"; + else { + var mcw = mc.offsetWidth; + if (typeof mcw == "undefined") + // Konqueror brain-dead techniques + mcw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px"; + } s.top = (cd.offsetTop + cd.offsetHeight) + "px"; }; @@ -294,15 +303,20 @@ Calendar.showYearsCombo = function (fwd) { yr.style.display = "none"; } yr = yr.nextSibling; - Y += fwd ? 2 : -2; + Y += fwd ? cal.yearStep : -cal.yearStep; } if (show) { var s = yc.style; s.display = "block"; if (cd.navtype < 0) s.left = cd.offsetLeft + "px"; - else - s.left = (cd.offsetLeft + cd.offsetWidth - yc.offsetWidth) + "px"; + else { + var ycw = yc.offsetWidth; + if (typeof ycw == "undefined") + // Konqueror brain-dead techniques + ycw = 50; + s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px"; + } s.top = (cd.offsetTop + cd.offsetHeight) + "px"; } }; @@ -397,9 +411,9 @@ Calendar.tableMouseOver = function (ev) { break; while (count-- > 0) if (decrease) { - if (!(--i in range)) + if (--i < 0) i = range.length - 1; - } else if (!(++i in range)) + } else if ( ++i >= range.length ) i = 0; var newval = range[i]; el.firstChild.data = newval; @@ -474,7 +488,6 @@ Calendar.calDragEnd = function (ev) { cal.dragging = false; with (Calendar) { removeEvent(document, "mousemove", calDragIt); - removeEvent(document, "mouseover", stopEvent); removeEvent(document, "mouseup", calDragEnd); tableMouseUp(ev); } @@ -490,11 +503,12 @@ Calendar.dayMouseDown = function(ev) { cal.activeDiv = el; Calendar._C = cal; if (el.navtype != 300) with (Calendar) { - if (el.navtype == 50) + if (el.navtype == 50) { el._current = el.firstChild.data; + addEvent(document, "mousemove", tableMouseOver); + } else + addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver); addClass(el, "hilite active"); - addEvent(document, "mouseover", tableMouseOver); - addEvent(document, "mousemove", tableMouseOver); addEvent(document, "mouseup", tableMouseUp); } else if (cal.isPopup) { cal._dragStart(ev); @@ -525,11 +539,7 @@ Calendar.dayMouseOver = function(ev) { } if (el.ttip) { if (el.ttip.substr(0, 1) == "_") { - var date = null; - with (el.calendar.date) { - date = new Date(getFullYear(), getMonth(), el.caldate); - } - el.ttip = date.print(el.calendar.ttDateFormat) + el.ttip.substr(1); + el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1); } el.calendar.tooltips.firstChild.data = el.ttip; } @@ -573,11 +583,12 @@ Calendar.cellClick = function(el, ev) { if (!closing) { cal.currentDateEl = el; } - cal.date.setDate(el.caldate); + cal.date = new Date(el.caldate); date = cal.date; newdate = true; // a date was clicked - cal.dateClicked = true; + if (!(cal.dateClicked = !el.otherMonth)) + cal._init(cal.firstDayOfWeek, date); } else { if (el.navtype == 200) { Calendar.removeClass(el, "hilite"); @@ -644,7 +655,7 @@ Calendar.cellClick = function(el, ev) { } break; case 100: - cal.setMondayFirst(!cal.mondayFirst); + cal.setFirstDayOfWeek(el.fdow); return; case 50: var range = el._range; @@ -653,9 +664,9 @@ Calendar.cellClick = function(el, ev) { if (range[i] == current) break; if (ev && ev.shiftKey) { - if (!(--i in range)) + if (--i < 0) i = range.length - 1; - } else if (!(++i in range)) + } else if ( ++i >= range.length ) i = 0; var newval = range[i]; el.firstChild.data = newval; @@ -824,7 +835,7 @@ Calendar.prototype.create = function (_par) { cell = Calendar.createElement("td", row); cell.className = "time"; cell.colSpan = 2; - cell.innerHTML = " "; + cell.innerHTML = Calendar._TT["TIME"] || " "; cell = Calendar.createElement("td", row); cell.className = "time"; @@ -941,7 +952,7 @@ Calendar.prototype.create = function (_par) { div.appendChild(yr); } - this._init(this.mondayFirst, this.date); + this._init(this.firstDayOfWeek, this.date); parent.appendChild(this.element); }; @@ -975,7 +986,7 @@ Calendar._keyEvent = function(ev) { Calendar.cellClick(cal._nav_now); break; case 27: // KEY esc - act && cal.hide(); + act && cal.callCloseHandler(); break; case 37: // KEY left case 38: // KEY up @@ -1014,7 +1025,7 @@ Calendar._keyEvent = function(ev) { } Calendar.removeClass(el, "selected"); Calendar.addClass(ne, "selected"); - cal.date.setDate(ne.caldate); + cal.date = new Date(ne.caldate); cal.callHandler(); cal.currentDateEl = ne; } @@ -1032,12 +1043,11 @@ Calendar._keyEvent = function(ev) { }; /** - * (RE)Initializes the calendar to the given date and style (if mondayFirst is - * true it makes Monday the first day of week, otherwise the weeks start on - * Sunday. + * (RE)Initializes the calendar to the given date and firstDayOfWeek */ -Calendar.prototype._init = function (mondayFirst, date) { +Calendar.prototype._init = function (firstDayOfWeek, date) { var today = new Date(); + this.table.style.visibility = "hidden"; var year = date.getFullYear(); if (year < this.minYear) { year = this.minYear; @@ -1046,53 +1056,57 @@ Calendar.prototype._init = function (mondayFirst, date) { year = this.maxYear; date.setFullYear(year); } - this.mondayFirst = mondayFirst; + this.firstDayOfWeek = firstDayOfWeek; this.date = new Date(date); var month = date.getMonth(); var mday = date.getDate(); var no_days = date.getMonthDays(); + + // calendar voodoo for computing the first day that would actually be + // displayed in the calendar, even if it's from the previous month. + // WARNING: this is magic. ;-) date.setDate(1); - var wday = date.getDay(); - var MON = mondayFirst ? 1 : 0; - var SAT = mondayFirst ? 5 : 6; - var SUN = mondayFirst ? 6 : 0; - if (mondayFirst) { - wday = (wday > 0) ? (wday - 1) : 6; - } - var iday = 1; + var day1 = (date.getDay() - this.firstDayOfWeek) % 7; + if (day1 < 0) + day1 += 7; + date.setDate(-day1); + date.setDate(date.getDate() + 1); + var row = this.tbody.firstChild; var MN = Calendar._SMN[month]; - var hasToday = ((today.getFullYear() == year) && (today.getMonth() == month)); - var todayDate = today.getDate(); - var week_number = date.getWeekNumber(); var ar_days = new Array(); - for (var i = 0; i < 6; ++i) { - if (iday > no_days) { - row.className = "emptyrow"; - row = row.nextSibling; - continue; - } + var weekend = Calendar._TT["WEEKEND"]; + for (var i = 0; i < 6; ++i, row = row.nextSibling) { var cell = row.firstChild; if (this.weekNumbers) { cell.className = "day wn"; - cell.firstChild.data = week_number; + cell.firstChild.data = date.getWeekNumber(); cell = cell.nextSibling; } - ++week_number; row.className = "daysrow"; - for (var j = 0; j < 7; ++j) { + var hasdays = false; + for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(date.getDate() + 1)) { + var iday = date.getDate(); + var wday = date.getDay(); cell.className = "day"; - if ((!i && j < wday) || iday > no_days) { - // cell.className = "emptycell"; - cell.innerHTML = " "; - cell.disabled = true; - cell = cell.nextSibling; - continue; + var current_month = (date.getMonth() == month); + if (!current_month) { + if (this.showsOtherMonths) { + cell.className += " othermonth"; + cell.otherMonth = true; + } else { + cell.className = "emptycell"; + cell.innerHTML = " "; + cell.disabled = true; + continue; + } + } else { + cell.otherMonth = false; + hasdays = true; } cell.disabled = false; cell.firstChild.data = iday; if (typeof this.getDateStatus == "function") { - date.setDate(iday); var status = this.getDateStatus(date, year, month, iday); if (status === true) { cell.className += " disabled"; @@ -1105,29 +1119,30 @@ Calendar.prototype._init = function (mondayFirst, date) { } if (!cell.disabled) { ar_days[ar_days.length] = cell; - cell.caldate = iday; + cell.caldate = new Date(date); cell.ttip = "_"; - if (iday == mday) { + if (current_month && iday == mday) { cell.className += " selected"; this.currentDateEl = cell; } - if (hasToday && (iday == todayDate)) { + if (date.getFullYear() == today.getFullYear() && + date.getMonth() == today.getMonth() && + iday == today.getDate()) { cell.className += " today"; cell.ttip += Calendar._TT["PART_TODAY"]; } - if (wday == SAT || wday == SUN) { - cell.className += " weekend"; + if (weekend.indexOf(wday.toString()) != -1) { + cell.className += cell.otherMonth ? " oweekend" : " weekend"; } } - ++iday; - ((++wday) ^ 7) || (wday = 0); - cell = cell.nextSibling; } - row = row.nextSibling; + if (!(hasdays || this.showsOtherMonths)) + row.className = "emptyrow"; } this.ar_days = ar_days; this.title.firstChild.data = Calendar._MN[month] + ", " + year; this.onSetTime(); + this.table.style.visibility = "visible"; // PROFILE // this.tooltips.firstChild.data = "Generated in " + ((new Date()) - today) + " ms"; }; @@ -1138,7 +1153,7 @@ Calendar.prototype._init = function (mondayFirst, date) { */ Calendar.prototype.setDate = function (date) { if (!date.equalsTo(this.date)) { - this._init(this.mondayFirst, date); + this._init(this.firstDayOfWeek, date); } }; @@ -1149,12 +1164,12 @@ Calendar.prototype.setDate = function (date) { * should * change. */ Calendar.prototype.refresh = function () { - this._init(this.mondayFirst, this.date); + this._init(this.firstDayOfWeek, this.date); }; -/** Modifies the "mondayFirst" parameter (EU/US style). */ -Calendar.prototype.setMondayFirst = function (mondayFirst) { - this._init(mondayFirst, this.date); +/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */ +Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) { + this._init(firstDayOfWeek, this.date); this._displayWeekdays(); }; @@ -1282,6 +1297,30 @@ Calendar.prototype.showAtElement = function (el, opts) { this.showAt(p.x, p.y + el.offsetHeight); return true; } + function fixPosition(box) { + if (box.x < 0) + box.x = 0; + if (box.y < 0) + box.y = 0; + var cp = document.createElement("div"); + var s = cp.style; + s.position = "absolute"; + s.right = s.bottom = s.width = s.height = "0px"; + document.body.appendChild(cp); + var br = Calendar.getAbsolutePos(cp); + document.body.removeChild(cp); + if (Calendar.is_ie) { + br.y += document.body.scrollTop; + br.x += document.body.scrollLeft; + } else { + br.y += window.scrollY; + br.x += window.scrollX; + } + var tmp = box.x + box.width - br.x; + if (tmp > 0) box.x -= tmp; + tmp = box.y + box.height - br.y; + if (tmp > 0) box.y -= tmp; + }; this.element.style.display = "block"; Calendar.continuation_for_the_fucking_khtml_browser = function() { var w = self.element.offsetWidth; @@ -1308,6 +1347,10 @@ Calendar.prototype.showAtElement = function (el, opts) { case "r": p.x += el.offsetWidth - w; break; case "l": break; // already there } + p.width = w; + p.height = h + 40; + self.monthsCombo.style.display = "none"; + fixPosition(p); self.showAt(p.x, p.y); }; if (Calendar.is_khtml) @@ -1338,38 +1381,52 @@ Calendar.prototype.parseDate = function (str, fmt) { if (!fmt) { fmt = this.dateFormat; } - var b = []; - fmt.replace(/(%.)/g, function(str, par) { - return b[b.length] = par; - }); + var b = fmt.match(/%./g); var i = 0, j = 0; var hr = 0; var min = 0; for (i = 0; i < a.length; ++i) { - if (b[i] == "%a" || b[i] == "%A") { + if (!a[i]) continue; - } - if (b[i] == "%d" || b[i] == "%e") { + switch (b[i]) { + case "%d": + case "%e": d = parseInt(a[i], 10); - } - if (b[i] == "%m") { + break; + + case "%m": m = parseInt(a[i], 10) - 1; - } - if (b[i] == "%Y" || b[i] == "%y") { + break; + + case "%Y": + case "%y": y = parseInt(a[i], 10); (y < 100) && (y += (y > 29) ? 1900 : 2000); - } - if (b[i] == "%b" || b[i] == "%B") { + break; + + case "%b": + case "%B": for (j = 0; j < 12; ++j) { if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; } } - } else if (/%[HIkl]/.test(b[i])) { + break; + + case "%H": + case "%I": + case "%k": + case "%l": hr = parseInt(a[i], 10); - } else if (/%[pP]/.test(b[i])) { + break; + + case "%P": + case "%p": if (/pm/i.test(a[i]) && hr < 12) hr += 12; - } else if (b[i] == "%M") { + break; + + case "%M": min = parseInt(a[i], 10); + break; } } if (y != 0 && m != -1 && d != 0) { @@ -1471,22 +1528,23 @@ Calendar.prototype.hideShowCovered = function () { /** Internal function; it displays the bar with the names of the weekday. */ Calendar.prototype._displayWeekdays = function () { - var MON = this.mondayFirst ? 0 : 1; - var SUN = this.mondayFirst ? 6 : 0; - var SAT = this.mondayFirst ? 5 : 6; + var fdow = this.firstDayOfWeek; var cell = this.firstdayname; + var weekend = Calendar._TT["WEEKEND"]; for (var i = 0; i < 7; ++i) { cell.className = "day name"; - if (!i) { - cell.ttip = this.mondayFirst ? Calendar._TT["SUN_FIRST"] : Calendar._TT["MON_FIRST"]; + var realday = (i + fdow) % 7; + if (i) { + cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]); cell.navtype = 100; cell.calendar = this; + cell.fdow = realday; Calendar._add_evs(cell); } - if (i == SUN || i == SAT) { + if (weekend.indexOf(realday.toString()) != -1) { Calendar.addClass(cell, "weekend"); } - cell.firstChild.data = Calendar._SDN[i + 1 - MON]; + cell.firstChild.data = Calendar._SDN[(i + fdow) % 7]; cell = cell.nextSibling; } }; @@ -1517,7 +1575,6 @@ Calendar.prototype._dragStart = function (ev) { this.yOffs = posY - parseInt(st.top); with (Calendar) { addEvent(document, "mousemove", calDragIt); - addEvent(document, "mouseover", stopEvent); addEvent(document, "mouseup", calDragEnd); } }; @@ -1550,20 +1607,20 @@ Date.prototype.getMonthDays = function(month) { /** Returns the number of day in the year. */ Date.prototype.getDayOfYear = function() { var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); - var then = new Date(this.getFullYear(), 0, 1, 0, 0, 0); + var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0); var time = now - then; return Math.floor(time / Date.DAY); }; /** Returns the number of the week in year, as defined in ISO 8601. */ Date.prototype.getWeekNumber = function() { - var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); - var then = new Date(this.getFullYear(), 0, 1, 0, 0, 0); - var time = now - then; - var day = then.getDay(); // 0 means Sunday - if (day == 0) day = 7; - (day > 4) && (day -= 4) || (day += 3); - return Math.round(((time / Date.DAY) + day) / 7); + var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0); + var DoW = d.getDay(); + d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu + var ms = d.valueOf(); // GMT + d.setMonth(0); + d.setDate(4); // Thu in Week 1 + return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1; }; /** Checks dates equality (ignores time) */ @@ -1625,17 +1682,34 @@ Date.prototype.print = function (str) { s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99) s["%Y"] = y; // year with the century s["%%"] = "%"; // a literal '%' character - var re = Date._msh_formatRegexp; - if (typeof re == "undefined") { - var tmp = ""; - for (var i in s) - tmp += tmp ? ("|" + i) : i; - Date._msh_formatRegexp = re = new RegExp("(" + tmp + ")", 'g'); - } - return str.replace(re, function(match, par) { return s[par]; }); + + var re = /%./g; + if (!Calendar.is_ie5) + return str.replace(re, function (par) { return s[par] || par; }); + + var a = str.match(re); + for (var i = 0; i < a.length; i++) { + var tmp = s[a[i]]; + if (tmp) { + re = new RegExp(a[i], 'g'); + str = str.replace(re, tmp); + } + } + + return str; +}; + +Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear; +Date.prototype.setFullYear = function(y) { + var d = new Date(this); + d.__msh_oldSetFullYear(y); + if (d.getMonth() != this.getMonth()) + this.setDate(28); + this.__msh_oldSetFullYear(y); }; // END: DATE OBJECT PATCHES + // global object that remembers the calendar window.calendar = null; diff --git a/httemplate/elements/calendar_stripped.js b/httemplate/elements/calendar_stripped.js index 029496a74..6a8e326af 100644 --- a/httemplate/elements/calendar_stripped.js +++ b/httemplate/elements/calendar_stripped.js @@ -1,7 +1,7 @@ /* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/ * ------------------------------------------------------------------ * - * The DHTML Calendar, version 0.9.5 "Your favorite time, bis" + * The DHTML Calendar, version 0.9.6 "Keep cool but don't freeze" * * Details and latest version at: * http://dynarch.com/mishoo/calendar.epl @@ -9,4 +9,4 @@ * This script is distributed under the GNU Lesser General Public License. * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html */ - Calendar=function(mondayFirst,dateStr,onSelected,onClose){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.timeout=null;this.onSelected=onSelected||null;this.onClose=onClose||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.mondayFirst=mondayFirst;this.dateStr=dateStr;this.ar_days=null;this.showsTime=false;this.time24=true;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined")Calendar._SDN_len=3;var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined")Calendar._SMN_len=3;ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)SL=el.scrollLeft;if(is_div&&el.scrollTop)ST=el.scrollTop;var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=Calendar.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}return r;};Calendar.isRelated=function(el,evt){var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}while(related){if(related==el){return true;}related=related.parentNode;}return false;};Calendar.removeClass=function(el,className){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,className){Calendar.removeClass(el,className);el.className+=" "+className;};Calendar.getElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.currentTarget;}};Calendar.getTargetElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.target;}};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,evname,func){if(el.attachEvent){el.attachEvent("on"+evname,func);}else if(el.addEventListener){el.addEventListener(evname,func,true);}else{el["on"+evname]=func;}};Calendar.removeEvent=function(el,evname,func){if(el.detachEvent){el.detachEvent("on"+evname,func);}else if(el.removeEventListener){el.removeEventListener(evname,func,true);}else{el["on"+evname]=null;}};Calendar.createElement=function(type,parent){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",type);}else{el=document.createElement(type);}if(typeof parent!="undefined"){parent.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else if(typeof el.parentNode.month!="undefined"){return el.parentNode;}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else if(typeof el.parentNode.year!="undefined"){return el.parentNode;}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else s.left=(cd.offsetLeft+cd.offsetWidth-mc.offsetWidth)+"px";s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var show=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.firstChild.data=Y;yr.year=Y;yr.style.display="block";show=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?2:-2;}if(show){var s=yc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else s.left=(cd.offsetLeft+cd.offsetWidth-yc.offsetWidth)+"px";s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var target=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(target==el||target.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(target);var date=null;if(mon){date=new Date(cal.date);if(mon.month!=date.getMonth()){date.setMonth(mon.month);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}else{var year=Calendar.findYear(target);if(year){date=new Date(cal.date);if(year.year!=date.getFullYear()){date.setFullYear(year.year);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var target=Calendar.getTargetElement(ev);if(target==el||target.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2)))Calendar.removeClass(el,"active");Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&target!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var decrease=true;if(x>pos.x+w){dx=x-pos.x-w;decrease=false;}else dx=pos.x-x;if(dx<0)dx=0;var range=el._range;var current=el._current;var count=Math.floor(dx/10)%range.length;for(var i=range.length;--i>=0;)if(range[i]==current)break;while(count-->0)if(decrease){if(!(--i in range))i=range.length-1;}else if(!(++i in range))i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();}var mon=Calendar.findMonth(target);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var year=Calendar.findYear(target);if(year){if(year.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(year,"hilite");cal.hilitedYear=year;}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posX=ev.pageX;posY=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(posX-cal.xOffs)+"px";st.top=(posY-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseover",stopEvent);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300)with(Calendar){if(el.navtype==50)el._current=el.firstChild.data;addClass(el,"hilite active");addEvent(document,"mouseover",tableMouseOver);addEvent(document,"mousemove",tableMouseOver);addEvent(document,"mouseup",tableMouseUp);}else if(cal.isPopup){cal._dragStart(ev);}if(el.navtype==-1||el.navtype==1){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else if(el.navtype==-2||el.navtype==2){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){var date=null;with(el.calendar.date){date=new Date(getFullYear(),getMonth(),el.caldate);}el.ttip=date.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.firstChild.data=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled){return false;}removeClass(el,"hilite");if(el.caldate){removeClass(el.parentNode,"rowhilite");}el.calendar.tooltips.firstChild.data=_TT["SEL_DATE"];return stopEvent(ev);}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var closing=false;var newdate=false;var date=null;if(typeof el.navtype=="undefined"){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");closing=(cal.currentDateEl==el);if(!closing){cal.currentDateEl=el;}cal.date.setDate(el.caldate);date=cal.date;newdate=true;cal.dateClicked=true;}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}date=(el.navtype==0)?new Date():new Date(cal.date);cal.dateClicked=false;var year=date.getFullYear();var mon=date.getMonth();function setMonth(m){var day=date.getDate();var max=date.getMonthDays(m);if(day>max){date.setDate(max);}date.setMonth(m);};switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var text=Calendar._TT["ABOUT"];if(typeof text!="undefined"){text+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{text="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mishoo@infoiasi.ro> to get it into the distribution ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(text);return;case-2:if(year>cal.minYear){date.setFullYear(year-1);}break;case-1:if(mon>0){setMonth(mon-1);}else if(year-->cal.minYear){date.setFullYear(year);setMonth(11);}break;case 1:if(mon<11){setMonth(mon+1);}else if(year<cal.maxYear){date.setFullYear(year+1);setMonth(0);}break;case 2:if(year<cal.maxYear){date.setFullYear(year+1);}break;case 100:cal.setMondayFirst(!cal.mondayFirst);return;case 50:var range=el._range;var current=el.firstChild.data;for(var i=range.length;--i>=0;)if(range[i]==current)break;if(ev&&ev.shiftKey){if(!(--i in range))i=range.length-1;}else if(!(++i in range))i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(date,date.getFullYear(),date.getMonth(),date.getDate())){return false;}break;}if(!date.equalsTo(cal.date)){cal.setDate(date);newdate=true;}}if(newdate){cal.callHandler();}if(closing){Calendar.removeClass(el,"hilite");cal.callCloseHandler();}};Calendar.prototype.create=function(_par){var parent=null;if(!_par){parent=document.getElementsByTagName("body")[0];this.isPopup=true;}else{parent=_par;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var table=Calendar.createElement("table");this.table=table;table.cellSpacing=0;table.cellPadding=0;table.calendar=this;Calendar.addEvent(table,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(table);var thead=Calendar.createElement("thead",table);var cell=null;var row=null;var cal=this;var hh=function(text,cs,navtype){cell=Calendar.createElement("td",row);cell.colSpan=cs;cell.className="button";if(navtype!=0&&Math.abs(navtype)<=2)cell.className+=" nav";Calendar._add_evs(cell);cell.calendar=cal;cell.navtype=navtype;if(text.substr(0,1)!="&"){cell.appendChild(document.createTextNode(text));}else{cell.innerHTML=text;}return cell;};row=Calendar.createElement("tr",thead);var title_length=6;(this.isPopup)&&--title_length;(this.weekNumbers)&&++title_length;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",title_length,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("×",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",thead);row.className="headrow";this._nav_py=hh("«",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("‹",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("›",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("»",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",thead);row.className="daynames";if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.className="name wn";cell.appendChild(document.createTextNode(Calendar._TT["WK"]));}for(var i=7;i>0;--i){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));if(!i){cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var tbody=Calendar.createElement("tbody",table);this.tbody=tbody;for(i=6;i>0;--i){row=Calendar.createElement("tr",tbody);if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));}for(var j=7;j>0;--j){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));cell.calendar=this;Calendar._add_evs(cell);}}if(this.showsTime){row=Calendar.createElement("tr",tbody);row.className="time";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;cell.innerHTML=" ";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(className,init,range_start,range_end){var part=Calendar.createElement("span",cell);part.className=className;part.appendChild(document.createTextNode(init));part.calendar=cal;part.ttip=Calendar._TT["TIME_PART"];part.navtype=50;part._range=[];if(typeof range_start!="number")part._range=range_start;else{for(var i=range_start;i<=range_end;++i){var txt;if(i<10&&range_end>=10)txt='0'+i;else txt=''+i;part._range[part._range.length]=txt;}}Calendar._add_evs(part);return part;};var hrs=cal.date.getHours();var mins=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm)hrs-=12;var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var span=Calendar.createElement("span",cell);span.appendChild(document.createTextNode(":"));span.className="colon";var M=makeTimePart("minute",mins,0,59);var AP=null;cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;if(t12)AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);else cell.innerHTML=" ";cal.onSetTime=function(){var hrs=this.date.getHours();var mins=this.date.getMinutes();var pm=(hrs>12);if(pm&&t12)hrs-=12;H.firstChild.data=(hrs<10)?("0"+hrs):hrs;M.firstChild.data=(mins<10)?("0"+mins):mins;if(t12)AP.firstChild.data=pm?"pm":"am";};cal.onUpdateTime=function(){var date=this.date;var h=parseInt(H.firstChild.data,10);if(t12){if(/pm/i.test(AP.firstChild.data)&&h<12)h+=12;else if(/am/i.test(AP.firstChild.data)&&h==12)h=0;}var d=date.getDate();var m=date.getMonth();var y=date.getFullYear();date.setHours(h);date.setMinutes(parseInt(M.firstChild.data,10));date.setFullYear(y);date.setMonth(m);date.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var tfoot=Calendar.createElement("tfoot",table);row=Calendar.createElement("tr",tfoot);row.className="footrow";cell=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);cell.className="ttip";if(this.isPopup){cell.ttip=Calendar._TT["DRAG_TO_MOVE"];cell.style.cursor="move";}this.tooltips=cell;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.appendChild(document.createTextNode(Calendar._SMN[i]));div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";yr.appendChild(document.createTextNode(""));div.appendChild(yr);}this._init(this.mondayFirst,this.date);parent.appendChild(this.element);};Calendar._keyEvent=function(ev){if(!window.calendar){return false;}(Calendar.is_ie)&&(ev=window.event);var cal=window.calendar;var act=(Calendar.is_ie||ev.type=="keypress");if(ev.ctrlKey){switch(ev.keyCode){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else switch(ev.keyCode){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.hide();break;case 37:case 38:case 39:case 40:if(act){var date=cal.date.getDate()-1;var el=cal.currentDateEl;var ne=null;var prev=(ev.keyCode==37)||(ev.keyCode==38);switch(ev.keyCode){case 37:(--date>=0)&&(ne=cal.ar_days[date]);break;case 38:date-=7;(date>=0)&&(ne=cal.ar_days[date]);break;case 39:(++date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;case 40:date+=7;(date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;}if(!ne){if(prev){Calendar.cellClick(cal._nav_pm);}else{Calendar.cellClick(cal._nav_nm);}date=(prev)?cal.date.getMonthDays():1;el=cal.currentDateEl;ne=cal.ar_days[date-1];}Calendar.removeClass(el,"selected");Calendar.addClass(ne,"selected");cal.date.setDate(ne.caldate);cal.callHandler();cal.currentDateEl=ne;}break;case 13:if(act){cal.callHandler();cal.hide();}break;default:return false;}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(mondayFirst,date){var today=new Date();var year=date.getFullYear();if(year<this.minYear){year=this.minYear;date.setFullYear(year);}else if(year>this.maxYear){year=this.maxYear;date.setFullYear(year);}this.mondayFirst=mondayFirst;this.date=new Date(date);var month=date.getMonth();var mday=date.getDate();var no_days=date.getMonthDays();date.setDate(1);var wday=date.getDay();var MON=mondayFirst?1:0;var SAT=mondayFirst?5:6;var SUN=mondayFirst?6:0;if(mondayFirst){wday=(wday>0)?(wday-1):6;}var iday=1;var row=this.tbody.firstChild;var MN=Calendar._SMN[month];var hasToday=((today.getFullYear()==year)&&(today.getMonth()==month));var todayDate=today.getDate();var week_number=date.getWeekNumber();var ar_days=new Array();for(var i=0;i<6;++i){if(iday>no_days){row.className="emptyrow";row=row.nextSibling;continue;}var cell=row.firstChild;if(this.weekNumbers){cell.className="day wn";cell.firstChild.data=week_number;cell=cell.nextSibling;}++week_number;row.className="daysrow";for(var j=0;j<7;++j){cell.className="day";if((!i&&j<wday)||iday>no_days){cell.innerHTML=" ";cell.disabled=true;cell=cell.nextSibling;continue;}cell.disabled=false;cell.firstChild.data=iday;if(typeof this.getDateStatus=="function"){date.setDate(iday);var status=this.getDateStatus(date,year,month,iday);if(status===true){cell.className+=" disabled";cell.disabled=true;}else{if(/disabled/i.test(status))cell.disabled=true;cell.className+=" "+status;}}if(!cell.disabled){ar_days[ar_days.length]=cell;cell.caldate=iday;cell.ttip="_";if(iday==mday){cell.className+=" selected";this.currentDateEl=cell;}if(hasToday&&(iday==todayDate)){cell.className+=" today";cell.ttip+=Calendar._TT["PART_TODAY"];}if(wday==SAT||wday==SUN){cell.className+=" weekend";}}++iday;((++wday)^ 7)||(wday=0);cell=cell.nextSibling;}row=row.nextSibling;}this.ar_days=ar_days;this.title.firstChild.data=Calendar._MN[month]+", "+year;this.onSetTime();};Calendar.prototype.setDate=function(date){if(!date.equalsTo(this.date)){this._init(this.mondayFirst,date);}};Calendar.prototype.refresh=function(){this._init(this.mondayFirst,this.date);};Calendar.prototype.setMondayFirst=function(mondayFirst){this._init(mondayFirst,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(unaryFunction){this.getDateStatus=unaryFunction;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window.calendar=null;};Calendar.prototype.reparent=function(new_parent){var el=this.element;el.parentNode.removeChild(el);new_parent.appendChild(el);};Calendar._checkCalendar=function(ev){if(!window.calendar){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=calendar.element;el=el.parentNode);if(el==null){window.calendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var rows=this.table.getElementsByTagName("tr");for(var i=rows.length;i>0;){var row=rows[--i];Calendar.removeClass(row,"rowhilite");var cells=row.getElementsByTagName("td");for(var j=cells.length;j>0;){var cell=cells[--j];Calendar.removeClass(cell,"hilite");Calendar.removeClass(cell,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window.calendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,opts){var self=this;var p=Calendar.getAbsolutePos(el);if(!opts||typeof opts!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=self.element.offsetWidth;var h=self.element.offsetHeight;self.element.style.display="none";var valign=opts.substr(0,1);var halign="l";if(opts.length>1){halign=opts.substr(1,1);}switch(valign){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(halign){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "r":p.x+=el.offsetWidth-w;break;case "l":break;}self.showAt(p.x,p.y);};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){var y=0;var m=-1;var d=0;var a=str.split(/\W+/);if(!fmt){fmt=this.dateFormat;}var b=[];fmt.replace(/(%.)/g,function(str,par){return b[b.length]=par;});var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(b[i]=="%a"||b[i]=="%A"){continue;}if(b[i]=="%d"||b[i]=="%e"){d=parseInt(a[i],10);}if(b[i]=="%m"){m=parseInt(a[i],10)-1;}if(b[i]=="%Y"||b[i]=="%y"){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}if(b[i]=="%b"||b[i]=="%B"){for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}}else if(/%[HIkl]/.test(b[i])){hr=parseInt(a[i],10);}else if(/%[pP]/.test(b[i])){if(/pm/i.test(a[i])&&hr<12)hr+=12;}else if(b[i]=="%M"){min=parseInt(a[i],10);}}if(y!=0&&m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));return;}y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else if(d==0){d=a[i];}}if(y==0){var today=new Date();y=today.getFullYear();}if(m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));}};Calendar.prototype.hideShowCovered=function(){var self=this;Calendar.continuation_for_the_fucking_khtml_browser=function(){function getVisib(obj){var value=obj.style.visibility;if(!value){if(document.defaultView&&typeof(document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml)value=document.defaultView. getComputedStyle(obj,"").getPropertyValue("visibility");else value='';}else if(obj.currentStyle){value=obj.currentStyle.visibility;}else value='';}return value;};var tags=new Array("applet","iframe","select");var el=self.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=tags.length;k>0;){var ar=document.getElementsByTagName(tags[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(self.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype._displayWeekdays=function(){var MON=this.mondayFirst?0:1;var SUN=this.mondayFirst?6:0;var SAT=this.mondayFirst?5:6;var cell=this.firstdayname;for(var i=0;i<7;++i){cell.className="day name";if(!i){cell.ttip=this.mondayFirst?Calendar._TT["SUN_FIRST"]:Calendar._TT["MON_FIRST"];cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}if(i==SUN||i==SAT){Calendar.addClass(cell,"weekend");}cell.firstChild.data=Calendar._SDN[i+1-MON];cell=cell.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posY=ev.clientY+window.scrollY;posX=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=posX-parseInt(st.left);this.yOffs=posY-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseover",stopEvent);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.prototype.getMonthDays=function(month){var year=this.getFullYear();if(typeof month=="undefined"){month=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1){return 29;}else{return Date._MD[month];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,1,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,1,0,0,0);var time=now-then;var day=then.getDay();if(day==0)day=7;(day>4)&&(day-=4)||(day+=3);return Math.round(((time/Date.DAY)+day)/7);};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=Date._msh_formatRegexp;if(typeof re=="undefined"){var tmp="";for(var i in s)tmp+=tmp?("|"+i):i;Date._msh_formatRegexp=re=new RegExp("("+tmp+")",'g');}return str.replace(re,function(match,par){return s[par];});};window.calendar=null;
\ No newline at end of file + Calendar=function(firstDayOfWeek,dateStr,onSelected,onClose){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.timeout=null;this.onSelected=onSelected||null;this.onClose=onClose||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.firstDayOfWeek=firstDayOfWeek;this.showsOtherMonths=false;this.dateStr=dateStr;this.ar_days=null;this.showsTime=false;this.time24=true;this.yearStep=2;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined")Calendar._SDN_len=3;var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined")Calendar._SMN_len=3;ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_ie5=(Calendar.is_ie&&/msie 5\.0/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)SL=el.scrollLeft;if(is_div&&el.scrollTop)ST=el.scrollTop;var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=this.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}return r;};Calendar.isRelated=function(el,evt){var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}while(related){if(related==el){return true;}related=related.parentNode;}return false;};Calendar.removeClass=function(el,className){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,className){Calendar.removeClass(el,className);el.className+=" "+className;};Calendar.getElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.currentTarget;}};Calendar.getTargetElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.target;}};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,evname,func){if(el.attachEvent){el.attachEvent("on"+evname,func);}else if(el.addEventListener){el.addEventListener(evname,func,true);}else{el["on"+evname]=func;}};Calendar.removeEvent=function(el,evname,func){if(el.detachEvent){el.detachEvent("on"+evname,func);}else if(el.removeEventListener){el.removeEventListener(evname,func,true);}else{el["on"+evname]=null;}};Calendar.createElement=function(type,parent){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",type);}else{el=document.createElement(type);}if(typeof parent!="undefined"){parent.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else if(typeof el.parentNode.month!="undefined"){return el.parentNode;}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else if(typeof el.parentNode.year!="undefined"){return el.parentNode;}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var mcw=mc.offsetWidth;if(typeof mcw=="undefined")mcw=50;s.left=(cd.offsetLeft+cd.offsetWidth-mcw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var show=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.firstChild.data=Y;yr.year=Y;yr.style.display="block";show=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?cal.yearStep:-cal.yearStep;}if(show){var s=yc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else{var ycw=yc.offsetWidth;if(typeof ycw=="undefined")ycw=50;s.left=(cd.offsetLeft+cd.offsetWidth-ycw)+"px";}s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var target=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(target==el||target.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(target);var date=null;if(mon){date=new Date(cal.date);if(mon.month!=date.getMonth()){date.setMonth(mon.month);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}else{var year=Calendar.findYear(target);if(year){date=new Date(cal.date);if(year.year!=date.getFullYear()){date.setFullYear(year.year);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var target=Calendar.getTargetElement(ev);if(target==el||target.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2)))Calendar.removeClass(el,"active");Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&target!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var decrease=true;if(x>pos.x+w){dx=x-pos.x-w;decrease=false;}else dx=pos.x-x;if(dx<0)dx=0;var range=el._range;var current=el._current;var count=Math.floor(dx/10)%range.length;for(var i=range.length;--i>=0;)if(range[i]==current)break;while(count-->0)if(decrease){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();}var mon=Calendar.findMonth(target);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var year=Calendar.findYear(target);if(year){if(year.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(year,"hilite");cal.hilitedYear=year;}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posX=ev.pageX;posY=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(posX-cal.xOffs)+"px";st.top=(posY-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300)with(Calendar){if(el.navtype==50){el._current=el.firstChild.data;addEvent(document,"mousemove",tableMouseOver);}else addEvent(document,Calendar.is_ie5?"mousemove":"mouseover",tableMouseOver);addClass(el,"hilite active");addEvent(document,"mouseup",tableMouseUp);}else if(cal.isPopup){cal._dragStart(ev);}if(el.navtype==-1||el.navtype==1){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else if(el.navtype==-2||el.navtype==2){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){el.ttip=el.caldate.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.firstChild.data=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled){return false;}removeClass(el,"hilite");if(el.caldate){removeClass(el.parentNode,"rowhilite");}el.calendar.tooltips.firstChild.data=_TT["SEL_DATE"];return stopEvent(ev);}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var closing=false;var newdate=false;var date=null;if(typeof el.navtype=="undefined"){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");closing=(cal.currentDateEl==el);if(!closing){cal.currentDateEl=el;}cal.date=new Date(el.caldate);date=cal.date;newdate=true;if(!(cal.dateClicked=!el.otherMonth))cal._init(cal.firstDayOfWeek,date);}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}date=(el.navtype==0)?new Date():new Date(cal.date);cal.dateClicked=false;var year=date.getFullYear();var mon=date.getMonth();function setMonth(m){var day=date.getDate();var max=date.getMonthDays(m);if(day>max){date.setDate(max);}date.setMonth(m);};switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var text=Calendar._TT["ABOUT"];if(typeof text!="undefined"){text+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{text="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mishoo@infoiasi.ro> to get it into the distribution ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(text);return;case-2:if(year>cal.minYear){date.setFullYear(year-1);}break;case-1:if(mon>0){setMonth(mon-1);}else if(year-->cal.minYear){date.setFullYear(year);setMonth(11);}break;case 1:if(mon<11){setMonth(mon+1);}else if(year<cal.maxYear){date.setFullYear(year+1);setMonth(0);}break;case 2:if(year<cal.maxYear){date.setFullYear(year+1);}break;case 100:cal.setFirstDayOfWeek(el.fdow);return;case 50:var range=el._range;var current=el.firstChild.data;for(var i=range.length;--i>=0;)if(range[i]==current)break;if(ev&&ev.shiftKey){if(--i<0)i=range.length-1;}else if(++i>=range.length)i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(date,date.getFullYear(),date.getMonth(),date.getDate())){return false;}break;}if(!date.equalsTo(cal.date)){cal.setDate(date);newdate=true;}}if(newdate){cal.callHandler();}if(closing){Calendar.removeClass(el,"hilite");cal.callCloseHandler();}};Calendar.prototype.create=function(_par){var parent=null;if(!_par){parent=document.getElementsByTagName("body")[0];this.isPopup=true;}else{parent=_par;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var table=Calendar.createElement("table");this.table=table;table.cellSpacing=0;table.cellPadding=0;table.calendar=this;Calendar.addEvent(table,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(table);var thead=Calendar.createElement("thead",table);var cell=null;var row=null;var cal=this;var hh=function(text,cs,navtype){cell=Calendar.createElement("td",row);cell.colSpan=cs;cell.className="button";if(navtype!=0&&Math.abs(navtype)<=2)cell.className+=" nav";Calendar._add_evs(cell);cell.calendar=cal;cell.navtype=navtype;if(text.substr(0,1)!="&"){cell.appendChild(document.createTextNode(text));}else{cell.innerHTML=text;}return cell;};row=Calendar.createElement("tr",thead);var title_length=6;(this.isPopup)&&--title_length;(this.weekNumbers)&&++title_length;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",title_length,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("×",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",thead);row.className="headrow";this._nav_py=hh("«",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("‹",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("›",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("»",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",thead);row.className="daynames";if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.className="name wn";cell.appendChild(document.createTextNode(Calendar._TT["WK"]));}for(var i=7;i>0;--i){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));if(!i){cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var tbody=Calendar.createElement("tbody",table);this.tbody=tbody;for(i=6;i>0;--i){row=Calendar.createElement("tr",tbody);if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));}for(var j=7;j>0;--j){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));cell.calendar=this;Calendar._add_evs(cell);}}if(this.showsTime){row=Calendar.createElement("tr",tbody);row.className="time";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;cell.innerHTML=Calendar._TT["TIME"]||" ";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(className,init,range_start,range_end){var part=Calendar.createElement("span",cell);part.className=className;part.appendChild(document.createTextNode(init));part.calendar=cal;part.ttip=Calendar._TT["TIME_PART"];part.navtype=50;part._range=[];if(typeof range_start!="number")part._range=range_start;else{for(var i=range_start;i<=range_end;++i){var txt;if(i<10&&range_end>=10)txt='0'+i;else txt=''+i;part._range[part._range.length]=txt;}}Calendar._add_evs(part);return part;};var hrs=cal.date.getHours();var mins=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm)hrs-=12;var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var span=Calendar.createElement("span",cell);span.appendChild(document.createTextNode(":"));span.className="colon";var M=makeTimePart("minute",mins,0,59);var AP=null;cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;if(t12)AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);else cell.innerHTML=" ";cal.onSetTime=function(){var hrs=this.date.getHours();var mins=this.date.getMinutes();var pm=(hrs>12);if(pm&&t12)hrs-=12;H.firstChild.data=(hrs<10)?("0"+hrs):hrs;M.firstChild.data=(mins<10)?("0"+mins):mins;if(t12)AP.firstChild.data=pm?"pm":"am";};cal.onUpdateTime=function(){var date=this.date;var h=parseInt(H.firstChild.data,10);if(t12){if(/pm/i.test(AP.firstChild.data)&&h<12)h+=12;else if(/am/i.test(AP.firstChild.data)&&h==12)h=0;}var d=date.getDate();var m=date.getMonth();var y=date.getFullYear();date.setHours(h);date.setMinutes(parseInt(M.firstChild.data,10));date.setFullYear(y);date.setMonth(m);date.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var tfoot=Calendar.createElement("tfoot",table);row=Calendar.createElement("tr",tfoot);row.className="footrow";cell=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);cell.className="ttip";if(this.isPopup){cell.ttip=Calendar._TT["DRAG_TO_MOVE"];cell.style.cursor="move";}this.tooltips=cell;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.appendChild(document.createTextNode(Calendar._SMN[i]));div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";yr.appendChild(document.createTextNode(""));div.appendChild(yr);}this._init(this.firstDayOfWeek,this.date);parent.appendChild(this.element);};Calendar._keyEvent=function(ev){if(!window.calendar){return false;}(Calendar.is_ie)&&(ev=window.event);var cal=window.calendar;var act=(Calendar.is_ie||ev.type=="keypress");if(ev.ctrlKey){switch(ev.keyCode){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else switch(ev.keyCode){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.callCloseHandler();break;case 37:case 38:case 39:case 40:if(act){var date=cal.date.getDate()-1;var el=cal.currentDateEl;var ne=null;var prev=(ev.keyCode==37)||(ev.keyCode==38);switch(ev.keyCode){case 37:(--date>=0)&&(ne=cal.ar_days[date]);break;case 38:date-=7;(date>=0)&&(ne=cal.ar_days[date]);break;case 39:(++date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;case 40:date+=7;(date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;}if(!ne){if(prev){Calendar.cellClick(cal._nav_pm);}else{Calendar.cellClick(cal._nav_nm);}date=(prev)?cal.date.getMonthDays():1;el=cal.currentDateEl;ne=cal.ar_days[date-1];}Calendar.removeClass(el,"selected");Calendar.addClass(ne,"selected");cal.date=new Date(ne.caldate);cal.callHandler();cal.currentDateEl=ne;}break;case 13:if(act){cal.callHandler();cal.hide();}break;default:return false;}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(firstDayOfWeek,date){var today=new Date();this.table.style.visibility="hidden";var year=date.getFullYear();if(year<this.minYear){year=this.minYear;date.setFullYear(year);}else if(year>this.maxYear){year=this.maxYear;date.setFullYear(year);}this.firstDayOfWeek=firstDayOfWeek;this.date=new Date(date);var month=date.getMonth();var mday=date.getDate();var no_days=date.getMonthDays();date.setDate(1);var day1=(date.getDay()-this.firstDayOfWeek)%7;if(day1<0)day1+=7;date.setDate(-day1);date.setDate(date.getDate()+1);var row=this.tbody.firstChild;var MN=Calendar._SMN[month];var ar_days=new Array();var weekend=Calendar._TT["WEEKEND"];for(var i=0;i<6;++i,row=row.nextSibling){var cell=row.firstChild;if(this.weekNumbers){cell.className="day wn";cell.firstChild.data=date.getWeekNumber();cell=cell.nextSibling;}row.className="daysrow";var hasdays=false;for(var j=0;j<7;++j,cell=cell.nextSibling,date.setDate(date.getDate()+1)){var iday=date.getDate();var wday=date.getDay();cell.className="day";var current_month=(date.getMonth()==month);if(!current_month){if(this.showsOtherMonths){cell.className+=" othermonth";cell.otherMonth=true;}else{cell.className="emptycell";cell.innerHTML=" ";cell.disabled=true;continue;}}else{cell.otherMonth=false;hasdays=true;}cell.disabled=false;cell.firstChild.data=iday;if(typeof this.getDateStatus=="function"){var status=this.getDateStatus(date,year,month,iday);if(status===true){cell.className+=" disabled";cell.disabled=true;}else{if(/disabled/i.test(status))cell.disabled=true;cell.className+=" "+status;}}if(!cell.disabled){ar_days[ar_days.length]=cell;cell.caldate=new Date(date);cell.ttip="_";if(current_month&&iday==mday){cell.className+=" selected";this.currentDateEl=cell;}if(date.getFullYear()==today.getFullYear()&&date.getMonth()==today.getMonth()&&iday==today.getDate()){cell.className+=" today";cell.ttip+=Calendar._TT["PART_TODAY"];}if(weekend.indexOf(wday.toString())!=-1){cell.className+=cell.otherMonth?" oweekend":" weekend";}}}if(!(hasdays||this.showsOtherMonths))row.className="emptyrow";}this.ar_days=ar_days;this.title.firstChild.data=Calendar._MN[month]+", "+year;this.onSetTime();this.table.style.visibility="visible";};Calendar.prototype.setDate=function(date){if(!date.equalsTo(this.date)){this._init(this.firstDayOfWeek,date);}};Calendar.prototype.refresh=function(){this._init(this.firstDayOfWeek,this.date);};Calendar.prototype.setFirstDayOfWeek=function(firstDayOfWeek){this._init(firstDayOfWeek,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(unaryFunction){this.getDateStatus=unaryFunction;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window.calendar=null;};Calendar.prototype.reparent=function(new_parent){var el=this.element;el.parentNode.removeChild(el);new_parent.appendChild(el);};Calendar._checkCalendar=function(ev){if(!window.calendar){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=calendar.element;el=el.parentNode);if(el==null){window.calendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var rows=this.table.getElementsByTagName("tr");for(var i=rows.length;i>0;){var row=rows[--i];Calendar.removeClass(row,"rowhilite");var cells=row.getElementsByTagName("td");for(var j=cells.length;j>0;){var cell=cells[--j];Calendar.removeClass(cell,"hilite");Calendar.removeClass(cell,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window.calendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,opts){var self=this;var p=Calendar.getAbsolutePos(el);if(!opts||typeof opts!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}function fixPosition(box){if(box.x<0)box.x=0;if(box.y<0)box.y=0;var cp=document.createElement("div");var s=cp.style;s.position="absolute";s.right=s.bottom=s.width=s.height="0px";document.body.appendChild(cp);var br=Calendar.getAbsolutePos(cp);document.body.removeChild(cp);if(Calendar.is_ie){br.y+=document.body.scrollTop;br.x+=document.body.scrollLeft;}else{br.y+=window.scrollY;br.x+=window.scrollX;}var tmp=box.x+box.width-br.x;if(tmp>0)box.x-=tmp;tmp=box.y+box.height-br.y;if(tmp>0)box.y-=tmp;};this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=self.element.offsetWidth;var h=self.element.offsetHeight;self.element.style.display="none";var valign=opts.substr(0,1);var halign="l";if(opts.length>1){halign=opts.substr(1,1);}switch(valign){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(halign){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "r":p.x+=el.offsetWidth-w;break;case "l":break;}p.width=w;p.height=h+40;self.monthsCombo.style.display="none";fixPosition(p);self.showAt(p.x,p.y);};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){var y=0;var m=-1;var d=0;var a=str.split(/\W+/);if(!fmt){fmt=this.dateFormat;}var b=fmt.match(/%./g);var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(!a[i])continue;switch(b[i]){case "%d":case "%e":d=parseInt(a[i],10);break;case "%m":m=parseInt(a[i],10)-1;break;case "%Y":case "%y":y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);break;case "%b":case "%B":for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}break;case "%H":case "%I":case "%k":case "%l":hr=parseInt(a[i],10);break;case "%P":case "%p":if(/pm/i.test(a[i])&&hr<12)hr+=12;break;case "%M":min=parseInt(a[i],10);break;}}if(y!=0&&m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));return;}y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else if(d==0){d=a[i];}}if(y==0){var today=new Date();y=today.getFullYear();}if(m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));}};Calendar.prototype.hideShowCovered=function(){var self=this;Calendar.continuation_for_the_fucking_khtml_browser=function(){function getVisib(obj){var value=obj.style.visibility;if(!value){if(document.defaultView&&typeof(document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml)value=document.defaultView. getComputedStyle(obj,"").getPropertyValue("visibility");else value='';}else if(obj.currentStyle){value=obj.currentStyle.visibility;}else value='';}return value;};var tags=new Array("applet","iframe","select");var el=self.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=tags.length;k>0;){var ar=document.getElementsByTagName(tags[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(self.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype._displayWeekdays=function(){var fdow=this.firstDayOfWeek;var cell=this.firstdayname;var weekend=Calendar._TT["WEEKEND"];for(var i=0;i<7;++i){cell.className="day name";var realday=(i+fdow)%7;if(i){cell.ttip=Calendar._TT["DAY_FIRST"].replace("%s",Calendar._DN[realday]);cell.navtype=100;cell.calendar=this;cell.fdow=realday;Calendar._add_evs(cell);}if(weekend.indexOf(realday.toString())!=-1){Calendar.addClass(cell,"weekend");}cell.firstChild.data=Calendar._SDN[(i+fdow)%7];cell=cell.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posY=ev.clientY+window.scrollY;posX=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=posX-parseInt(st.left);this.yOffs=posY-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.prototype.getMonthDays=function(month){var year=this.getFullYear();if(typeof month=="undefined"){month=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1){return 29;}else{return Date._MD[month];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,0,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var d=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var DoW=d.getDay();d.setDate(d.getDate()-(DoW+6)%7+3);var ms=d.valueOf();d.setMonth(0);d.setDate(4);return Math.round((ms-d.valueOf())/(7*864e5))+1;};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=/%./g;if(!Calendar.is_ie5)return str.replace(re,function(par){return s[par]||par;});var a=str.match(re);for(var i=0;i<a.length;i++){var tmp=s[a[i]];if(tmp){re=new RegExp(a[i],'g');str=str.replace(re,tmp);}}return str;};Date.prototype.__msh_oldSetFullYear=Date.prototype.setFullYear;Date.prototype.setFullYear=function(y){var d=new Date(this);d.__msh_oldSetFullYear(y);if(d.getMonth()!=this.getMonth())this.setDate(28);this.__msh_oldSetFullYear(y);};window.calendar=null;
\ No newline at end of file diff --git a/httemplate/images/mid-logo.png b/httemplate/images/mid-logo.png Binary files differdeleted file mode 100644 index d993419cc..000000000 --- a/httemplate/images/mid-logo.png +++ /dev/null diff --git a/httemplate/images/small-logo.png b/httemplate/images/small-logo.png Binary files differindex 406a36980..a8fe80791 100644 --- a/httemplate/images/small-logo.png +++ b/httemplate/images/small-logo.png diff --git a/httemplate/index.html b/httemplate/index.html index 3e3543c79..00fe2da4c 100644 --- a/httemplate/index.html +++ b/httemplate/index.html @@ -7,9 +7,9 @@ <BODY BGCOLOR="#FFFFFF"> <table width="100%"> <tr><td> - <IMG BORDER=0 ALT="Silicon Interactive Software Design" SRC="images/small-logo.png"> - </td><td> - <font color="#ff0000" size=7>freeside main menu</font> + <IMG BORDER=0 ALT="freeside" SRC="images/small-logo.png"> + </td><td valign="top"> + <font color="#7f007b" size=7></font> </td><td align=right valign=bottom> version %%%VERSION%%% <BR><A HREF="http://www.sisd.com/freeside">Freeside home page</A> @@ -34,8 +34,7 @@ <FORM ACTION="search/cust_main.cgi" METHOD="POST"><INPUT TYPE="hidden" NAME="phone_on" VALUE="1">Phone # <INPUT TYPE="text" NAME="phone_text"><INPUT TYPE="submit" VALUE="Search"></FORM> <BR><FORM ACTION="search/svc_acct.cgi" METHOD="POST">Username <INPUT TYPE="text" NAME="username"><SELECT NAME="username_type"><OPTION VALUE="All">(all)</OPTION><OPTION>Fuzzy</OPTION><OPTION>Substring</OPTION><OPTION SELECTED>Exact</OPTION></SELECT><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_acct.cgi?username">all accounts by username</A> or <A HREF="search/svc_acct.cgi?uid">uid</A></FORM> <BR><FORM ACTION="search/svc_domain.cgi" METHOD="POST">Domain <INPUT TYPE="text" NAME="domain"><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_domain.cgi?domain">all domains</A></FORM> -<!-- <LI><A HREF="search/svc_acct_sm.html">mail aliases (by domain, and optionally username)</A>--> -<!-- <LI><A HREF="search/svc_forward.html">mail forwards (by ?)</A>--> + <BR><A HREF="search/svc_forward.cgi?svcnum">all mail forwards by svcnum</A><BR> <BR> </TD></TR> </TABLE> @@ -62,6 +61,7 @@ <UL> <LI><a href="search/cust_bill_event.html">Invoice event errors (failed credit cards)</a> <LI>open invoices (<A HREF="search/cust_bill.cgi?OPEN_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN_custnum">by customer number</A>) + <LI>15 day open invoices (<A HREF="search/cust_bill.cgi?OPEN15_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN15_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN15_custnum">by customer number</A>) <LI>30 day open invoices (<A HREF="search/cust_bill.cgi?OPEN30_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN30_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN30_custnum">by customer number</A>) <LI>60 day open invoices (<A HREF="search/cust_bill.cgi?OPEN60_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN60_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN60_custnum">by customer number</A>) <LI>90 day open invoices (<A HREF="search/cust_bill.cgi?OPEN90_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN90_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN90_custnum">by customer number</A>) @@ -69,13 +69,11 @@ <LI>all invoices (<A HREF="search/cust_bill.cgi?invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?date">by date</A>) (<A HREF="search/cust_bill.cgi?custnum">by customer number</A>) </UL> <A HREF="search/report_cust_pay.html">Payment report (by type and/or date range)</A> + <BR><BR><A HREF="search/report_cust_credit.html">Credit report (by employee and/or date range)</A> <BR><BR><A HREF="search/report_receivables.cgi">Accounts Receivable Aging Summary</A> - <BR><BR>(old) Financial reports (being rewritten) - <UL> - <LI> <A HREF="search/report_tax.html">tax reports</A> - <LI> <A HREF="search/report_cc.html">credit card receipts</A> - <LI> <A HREF="search/report_credit.html">credit memos</A> - </UL> + <BR><BR><A HREF="search/report_prepaid_income.html">Prepaid Income (Unearned Revenue) Report</A> + <BR><BR><A HREF="search/report_tax.html">Sales Tax Liability Report</A> + <BR><BR> <CENTER><HR WIDTH="94%" NOSHADE></CENTER><BR> <A NAME="admin">Administration</a> <ul> @@ -127,6 +125,7 @@ <LI><A HREF="search/cust_pkg_report.cgi">packages (by next bill date range)</A> </UL> <A HREF="browse/part_pkg.cgi?active=1">Package definitions (by number of active packages)</A><BR><BR> + <A HREF="browse/part_svc.cgi?active=1">Service definitions (by number of active services)</A><BR><BR> Customers <UL> <LI><A HREF="search/cust_main-otaker.cgi">Search customers by order-taker</A> @@ -154,6 +153,7 @@ <BR><A HREF="browse/queue.cgi">View pending job queue</A> <BR><A HREF="misc/cust_main-import.cgi">Batch import customers from CSV file</A> <BR><A HREF="misc/cust_main-import_charges.cgi">Batch import charges from CSV file</A> + <BR><A HREF="misc/dump.cgi">Download database dump</A> <BR><BR><CENTER><HR WIDTH="94%" NOSHADE></CENTER><BR> <A NAME="config" HREF="config/config-view.cgi">Configuration</a><!-- - <font size="+2" color="#ff0000">start here</font> --> <BR><BR><A NAME="admin">Administration</a> diff --git a/httemplate/misc/cancel-unaudited.cgi b/httemplate/misc/cancel-unaudited.cgi index 11cde968d..43e439b58 100755 --- a/httemplate/misc/cancel-unaudited.cgi +++ b/httemplate/misc/cancel-unaudited.cgi @@ -12,10 +12,13 @@ my $svcnum = $1; my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); die "Unknown svcnum!" unless $cust_svc; -&eidiot(qq!This account has already been audited. Cancel the - <A HREF="!. popurl(2). qq!view/cust_pkg.cgi?! . $cust_svc->getfield('pkgnum') . - qq!pkgnum"> package</A> instead.!) - if $cust_svc->pkgnum ne '' && $cust_svc->pkgnum ne '0'; +my $cust_pkg = $cust_svc->cust_pkg; +if ( $cust_pkg ) { + &eidiot( 'This account has already been audited. Cancel the '. + qq!<A HREF="${p}view/cust_main.cgi?!. $cust_pkg->custnum. + '#cust_pkg'. $cust_pkg->pkgnum. '">'. + 'package</A> instead.'); +} my $error = $cust_svc->cancel; diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi index 526e128a4..257c3384f 100755 --- a/httemplate/misc/cust_main-cancel.cgi +++ b/httemplate/misc/cust_main-cancel.cgi @@ -7,8 +7,8 @@ my $custnum = $1; my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); -my $error = $cust_main->cancel; -eidiot($error) if $error; +my @errors = $cust_main->cancel; +eidiot(join(' / ', @errors)) if scalar(@errors); #print $cgi->redirect($p. "view/cust_main.cgi?". $cust_main->custnum); print $cgi->redirect($p); diff --git a/httemplate/misc/dump.cgi b/httemplate/misc/dump.cgi new file mode 100644 index 000000000..dc1323bb3 --- /dev/null +++ b/httemplate/misc/dump.cgi @@ -0,0 +1,19 @@ +<% + if ( driver_name =~ /^Pg$/ ) { + my $dbname = (split(':', datasrc))[2]; + if ( $dbname =~ /[;=]/ ) { + my %elements = map { /^(\w+)=(.*)$/; $1=>$2 } split(';', $dbname); + $dbname = $elements{'dbname'}; + } + open(DUMP,"pg_dump $dbname |"); + } else { + eidiot "don't (yet) know how to dump ". driver_name. " databases\n"; + } + + http_header('Content-Type' => 'text/plain' ); + + while (<DUMP>) { + print $_; + } + close DUMP; +%> diff --git a/httemplate/misc/email-invoice.cgi b/httemplate/misc/email-invoice.cgi new file mode 100755 index 000000000..7ab1613ee --- /dev/null +++ b/httemplate/misc/email-invoice.cgi @@ -0,0 +1,23 @@ +<% + +my $conf = new FS::Conf; + +#untaint invnum +my($query) = $cgi->keywords; +$query =~ /^(\d*)$/; +my $invnum = $1; +my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); +die "Can't find invoice!\n" unless $cust_bill; + +my $error = send_email( + 'from' => $conf->config('invoice_from'), + 'to' => [ grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ], + 'subject' => 'Invoice', + 'body' => [ $cust_bill->print_text ], +); +eidiot($error) if $error; + +my $custnum = $cust_bill->getfield('custnum'); +print $cgi->redirect("${p}view/cust_main.cgi?$custnum"); + +%> diff --git a/httemplate/misc/expire_pkg.cgi b/httemplate/misc/expire_pkg.cgi index 9e4ce8b62..b59674a69 100755 --- a/httemplate/misc/expire_pkg.cgi +++ b/httemplate/misc/expire_pkg.cgi @@ -1,25 +1,55 @@ +<!-- mason kludge --> <% -#untaint date & pkgnum - -my $date; -if ( $cgi->param('date') ) { - str2time($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date"; - $date=$1; -} else { - $date=''; -} - -$cgi->param('pkgnum') =~ /^(\d+)$/ or die "Illegal pkgnum"; +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/; my $pkgnum = $1; +#get package record my $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); -my %hash = $cust_pkg->hash; -$hash{expire}=$date; -my $new = new FS::cust_pkg ( \%hash ); -my $error = $new->replace($cust_pkg); -&eidiot($error) if $error; +die "Unknown pkgnum $pkgnum" unless $cust_pkg; +my $part_pkg = $cust_pkg->part_pkg; + +my $custnum = $cust_pkg->getfield('custnum'); -print $cgi->redirect(popurl(2). "view/cust_main.cgi?".$cust_pkg->getfield('custnum')); +my $date = $cust_pkg->expire ? time2str('%D', $cust_pkg->expire) : ''; %> + +<%= header('Expire package', menubar( + "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + 'Main Menu' => popurl(2) +)) %> + +<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> + +<%= $pkgnum %>: <%= $part_pkg->pkg. ' - '. $part_pkg->comment %> + +<FORM NAME="formname" ACTION="process/expire_pkg.cgi" METHOD="post"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>"> +<TABLE> + <TR> + <TD>Cancel package on </TD> + <TD><INPUT TYPE="text" NAME="date" ID="expire_date" VALUE="<%= $date %>"> + <IMG SRC="<%= $p %>images/calendar.png" ID="expire_button" STYLE="cursor:pointer" TITLE="Select date"> + <BR><I>m/d/y</I> + </TD> + </TR> +</TABLE> + +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "expire_date", + ifFormat: "%m/%d/%Y", + button: "expire_button", + align: "BR" + }); +</SCRIPT> + +<INPUT TYPE="submit" VALUE="Cancel later"> +</FORM> +</BODY> +</HTML> diff --git a/httemplate/misc/link.cgi b/httemplate/misc/link.cgi index efc762cc5..18cd378d3 100755 --- a/httemplate/misc/link.cgi +++ b/httemplate/misc/link.cgi @@ -4,9 +4,16 @@ my %link_field = ( 'svc_acct' => 'username', 'svc_domain' => 'domain', - 'svc_acct_sm' => '', - 'svc_charge' => '', - 'svc_wo' => '', +); + +my %link_field2 = ( + 'svc_acct' => { label => 'Domain', + field => 'domsvc', + type => 'select', + select_table => 'svc_domain', + select_key => 'svcnum', + select_label => 'domain' + }, ); my($query) = $cgi->keywords; @@ -20,27 +27,48 @@ my $part_svc = qsearchs('part_svc',{'svcpart'=>$svcpart}); my $svc = $part_svc->getfield('svc'); my $svcdb = $part_svc->getfield('svcdb'); my $link_field = $link_field{$svcdb}; +my $link_field2 = $link_field2{$svcdb}; + +%> -print header("Link to existing $svc"), - qq!<FORM ACTION="!, popurl(1), qq!process/link.cgi" METHOD=POST>!; +<%= header("Link to existing $svc") %> +<FORM ACTION="<%= popurl(1) %>process/link.cgi" METHOD=POST> -if ( $link_field ) { - print <<END; +<% if ( $link_field ) { %> <INPUT TYPE="hidden" NAME="svcnum" VALUE=""> - <INPUT TYPE="hidden" NAME="link_field" VALUE="$link_field"> - $link_field of existing service: <INPUT TYPE="text" NAME="link_value"> -END -} else { - print qq!Service # of existing service: <INPUT TYPE="text" NAME="svcnum" VALUE="">!; -} + <INPUT TYPE="hidden" NAME="link_field" VALUE="<%= $link_field %>"> + <%= $link_field %> of existing service: <INPUT TYPE="text" NAME="link_value"> + <BR> + <% if ( $link_field2 ) { %> + <INPUT TYPE="hidden" NAME="link_field2" VALUE="<%= $link_field2->{field} %>"> + <%= $link_field2->{'label'} %> of existing service: + <% if ( $link_field2->{'type'} eq 'select' ) { %> + <% if ( $link_field2->{'select_table'} ) { %> + <SELECT NAME="link_value2"> + <OPTION> </OPTION> + <% foreach my $r ( qsearch( $link_field2->{'select_table'}, {})) { %> + <% my $key = $link_field2->{'select_key'}; %> + <% my $label = $link_field2->{'select_label'}; %> + <OPTION VALUE="<%= $r->$key() %>"><%= $r->$label() %></OPTION> + <% } %> + </SELECT> + <% } else { %> + Don't know how to process secondary link field for <%= $svcdb %> + (type=>select but no select_table) + <% } %> + <% } else { %> + Don't know how to process secondary link field for <%= $svcdb %> + (unknown type <%= $link_field2->{'type'} %>) + <% } %> + <BR> + <% } %> +<% } else { %> + Service # of existing service: <INPUT TYPE="text" NAME="svcnum" VALUE=""> +<% } %> -print <<END; -<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum"> -<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart"> -<P><CENTER><INPUT TYPE="submit" VALUE="Link"></CENTER> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>"> +<BR><INPUT TYPE="submit" VALUE="Link"> </FORM> </BODY> </HTML> -END - -%> diff --git a/httemplate/misc/print-invoice.cgi b/httemplate/misc/print-invoice.cgi index 0dda68a4e..144f6156a 100755 --- a/httemplate/misc/print-invoice.cgi +++ b/httemplate/misc/print-invoice.cgi @@ -24,6 +24,6 @@ die "Can't find invoice!\n" unless $cust_bill; my $custnum = $cust_bill->getfield('custnum'); -print $cgi->redirect(popurl(2). "view/cust_main.cgi?$custnum#history"); +print $cgi->redirect("${p}view/cust_main.cgi?$custnum"); %> diff --git a/httemplate/misc/process/expire_pkg.cgi b/httemplate/misc/process/expire_pkg.cgi new file mode 100755 index 000000000..dc35592ce --- /dev/null +++ b/httemplate/misc/process/expire_pkg.cgi @@ -0,0 +1,25 @@ +<% + +#untaint date & pkgnum + +my $date; +if ( $cgi->param('date') ) { + str2time($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date"; + $date=$1; +} else { + $date=''; +} + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die "Illegal pkgnum"; +my $pkgnum = $1; + +my $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +my %hash = $cust_pkg->hash; +$hash{expire}=$date; +my $new = new FS::cust_pkg ( \%hash ); +my $error = $new->replace($cust_pkg); +&eidiot($error) if $error; + +print $cgi->redirect(popurl(3). "view/cust_main.cgi?".$cust_pkg->getfield('custnum')); + +%> diff --git a/httemplate/misc/process/link.cgi b/httemplate/misc/process/link.cgi index 5d80adeb9..32a5360d9 100755 --- a/httemplate/misc/process/link.cgi +++ b/httemplate/misc/process/link.cgi @@ -12,8 +12,12 @@ unless ( $svcnum ) { my $svcdb = $part_svc->getfield('svcdb'); $cgi->param('link_field') =~ /^(\w+)$/; my $link_field = $1; + my %search = ( $link_field => $cgi->param('link_value') ); + if ( $cgi->param('link_field2') =~ /^(\w+)$/ ) { + $search{$1} = $cgi->param('link_value2'); + } my $svc_x = ( grep { $_->cust_svc->svcpart == $svcpart } - qsearch( $svcdb, { $link_field => $cgi->param('link_value') }) + qsearch( $svcdb, \%search ) )[0]; eidiot("$link_field not found!") unless $svc_x; $svcnum = $svc_x->svcnum; @@ -21,18 +25,25 @@ unless ( $svcnum ) { my $old = qsearchs('cust_svc',{'svcnum'=>$svcnum}); die "svcnum not found!" unless $old; -#die "svcnum $svcnum already linked to package ". $old->pkgnum if $old->pkgnum; -my $new = new FS::cust_svc ({ - 'svcnum' => $svcnum, - 'pkgnum' => $pkgnum, - 'svcpart' => $svcpart, -}); +my $conf = new FS::Conf; +my($error, $new); +if ( $old->pkgnum && ! $conf->exists('legacy_link-steal') ) { + $error = "svcnum $svcnum already linked to package ". $old->pkgnum; +} else { + $new = new FS::cust_svc ({ + 'svcnum' => $svcnum, + 'pkgnum' => $pkgnum, + 'svcpart' => $svcpart, + }); -my $error = $new->replace($old); + $error = $new->replace($old); +} unless ($error) { #no errors, so let's view this customer. - print $cgi->redirect(popurl(3). "view/cust_pkg.cgi?$pkgnum"); + my $custnum = $new->cust_pkg->custnum; + print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum". + "#cust_pkg$pkgnum" ); } else { %> <!-- mason kludge --> diff --git a/httemplate/misc/unapply-cust_credit.cgi b/httemplate/misc/unapply-cust_credit.cgi new file mode 100755 index 000000000..c658d2acc --- /dev/null +++ b/httemplate/misc/unapply-cust_credit.cgi @@ -0,0 +1,18 @@ +<% + +#untaint crednum +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/ || die "Illegal crednum"; +my $crednum = $1; + +my $cust_credit = qsearchs('cust_credit', { 'crednum' => $crednum } ); +my $custnum = $cust_credit->custnum; + +foreach my $cust_credit_bill ( $cust_credit->cust_credit_bill ) { + my $error = $cust_credit_bill->delete; + eidiot($error) if $error; +} + +print $cgi->redirect($p. "view/cust_main.cgi?". $custnum); + +%> diff --git a/httemplate/misc/unprovision.cgi b/httemplate/misc/unprovision.cgi index 8f2a7d13d..3c92a4e2e 100755 --- a/httemplate/misc/unprovision.cgi +++ b/httemplate/misc/unprovision.cgi @@ -12,10 +12,6 @@ my $svcnum = $1; my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); die "Unknown svcnum!" unless $cust_svc; -#&eidiot(qq!This account has already been audited. Cancel the -# <A HREF="!. popurl(2). qq!view/cust_pkg.cgi?! . $cust_svc->getfield('pkgnum') . -# qq!pkgnum"> package</A> instead.!) -# if $cust_svc->pkgnum ne '' && $cust_svc->pkgnum ne '0'; my $custnum = $cust_svc->cust_pkg->custnum; diff --git a/httemplate/misc/upload-batch.cgi b/httemplate/misc/upload-batch.cgi index cc5346606..5d0150177 100644 --- a/httemplate/misc/upload-batch.cgi +++ b/httemplate/misc/upload-batch.cgi @@ -2,8 +2,9 @@ my $fh = $cgi->upload('batch_results'); my $filename = $cgi->param('batch_results'); - $filename =~ /^.*[\/\\]([^\/\\]+)$/ or die; - my $paybatch = $1; + $filename =~ /^(.*[\/\\])?([^\/\\]+)$/ + or die "unparsable filename: $filename\n"; + my $paybatch = $2; my $error = defined($fh) ? FS::cust_pay_batch::import_results( { diff --git a/httemplate/misc/whois.cgi b/httemplate/misc/whois.cgi new file mode 100644 index 000000000..dd7851dc2 --- /dev/null +++ b/httemplate/misc/whois.cgi @@ -0,0 +1,25 @@ +<% + my $svcnum = $cgi->param('svcnum'); + my $custnum = $cgi->param('custnum'); + my $domain = $cgi->param('domain'); + +%> +<%= header("Whois $domain", menubar( + ( $custnum + ? ( "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + ) + : () + ), + "View this domain (#$svcnum)" => "${p}view/svc_domain.cgi?$svcnum", + "Main menu" => $p, +)) %> +<% my $whois = eval { whois($domain) }; + if ( $@ ) { + ( $whois = $@ ) =~ s/ at \/.*Net\/Whois\/Raw\.pm line \d+.*$//s; + } else { + $whois =~ s/^\n+//; + } +%> +<PRE><%= $whois %></PRE> +</BODY> +</HTML> diff --git a/httemplate/search/cust_bill.cgi b/httemplate/search/cust_bill.cgi index 985e3dbf5..5b0538ca3 100755 --- a/httemplate/search/cust_bill.cgi +++ b/httemplate/search/cust_bill.cgi @@ -125,10 +125,10 @@ END my $view = popurl(2). "view/cust_bill.cgi?$invnum"; print <<END; <TR> - <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$invnum</FONT></A></TD> - <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view"><FONT SIZE=-1>\$$owed</FONT></A></TD> - <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view"><FONT SIZE=-1>\$$charged</FONT></A></TD> - <TD ROWSPAN=$rowspan><A HREF="$view"><FONT SIZE=-1>$pdate</FONT></A></TD> + <TD ROWSPAN=$rowspan><A HREF="$view">$invnum</A></TD> + <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view">\$$owed</A></TD> + <TD ROWSPAN=$rowspan ALIGN="right"><A HREF="$view">\$$charged</A></TD> + <TD ROWSPAN=$rowspan><A HREF="$view">$pdate</A></TD> END my $custnum = $cust_bill->custnum; my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); @@ -139,8 +139,8 @@ END $cust_main->company, ); print <<END; - <TD ROWSPAN=$rowspan><A HREF="$cview"><FONT SIZE=-1>$name</FONT></A></TD> - <TD ROWSPAN=$rowspan><A HREF="$cview"><FONT SIZE=-1>$company</FONT></A></TD> + <TD ROWSPAN=$rowspan><A HREF="$cview">$name</A></TD> + <TD ROWSPAN=$rowspan><A HREF="$cview">$company</A></TD> END } else { print <<END @@ -153,8 +153,8 @@ END $tot_balance = sprintf("%.2f", $tot_balance); $tot_amount = sprintf("%.2f", $tot_amount); print "</TABLE>$pager<BR>". table(). <<END; - <TR><TD> </TD><TH><FONT SIZE=-1>Total<BR>Balance</FONT></TH><TH><FONT SIZE=-1>Total<BR>Amount</FONT></TH></TR> - <TR><TD></TD><TD ALIGN="right"><FONT SIZE=-1>\$$tot_balance</FONT></TD><TD ALIGN="right"><FONT SIZE=-1>\$$tot_amount</FONT></TD></TD></TR> + <TR><TD> </TD><TH>Total<BR>Balance</TH><TH>Total<BR>Amount</TH></TR> + <TR><TD></TD><TD ALIGN="right">\$$tot_balance</TD><TD ALIGN="right">\$$tot_amount</TD></TD></TR> </TABLE> </BODY> </HTML> diff --git a/httemplate/search/cust_bill_event.cgi b/httemplate/search/cust_bill_event.cgi index b76f66b76..ec952ea5b 100644 --- a/httemplate/search/cust_bill_event.cgi +++ b/httemplate/search/cust_bill_event.cgi @@ -7,7 +7,7 @@ $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/; my $beginning = str2time($1) || 0; $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/; -my $ending = str2time($1) + 86400; +my $ending = str2time($1) + 86399; my @cust_bill_event = sort { $a->_date <=> $b->_date } diff --git a/httemplate/search/cust_main-otaker.cgi b/httemplate/search/cust_main-otaker.cgi index b7173c49c..44214368a 100755 --- a/httemplate/search/cust_main-otaker.cgi +++ b/httemplate/search/cust_main-otaker.cgi @@ -10,10 +10,9 @@ <FORM ACTION="cust_main.cgi" METHOD="post"> Search for <B>Order taker</B>: <INPUT TYPE="hidden" NAME="otaker_on" VALUE="TRUE"> - <% my $dbh = dbh; - my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_main") - or eidiot $dbh->errstr; - $sth->execute() or eidiot $sth->errstr; + <% my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_main") + or die dbh->errstr; + $sth->execute() or die $sth->errstr; # my @otakers = map { $_->[0] } @{$sth->selectall_arrayref}; %> <SELECT NAME="otaker"> diff --git a/httemplate/search/cust_main-quickpay.html b/httemplate/search/cust_main-quickpay.html index d48f1d08f..077d290d9 100755 --- a/httemplate/search/cust_main-quickpay.html +++ b/httemplate/search/cust_main-quickpay.html @@ -13,19 +13,19 @@ <INPUT TYPE="checkbox" NAME="last_on" CHECKED> Search for <B>last name</B>: <INPUT TYPE="text" NAME="last_text"> using search method: <SELECT NAME="last_type"> - <OPTION>All + <OPTION SELECTED>All <OPTION>Fuzzy <OPTION>Substring - <OPTION SELECTED>Exact + <OPTION>Exact </SELECT> <P><INPUT TYPE="checkbox" NAME="company_on" CHECKED> Search for <B>company</B>: <INPUT TYPE="text" NAME="company_text"> - using search methods: <SELECT NAME="company_type"> - <OPTION>All + using search method: <SELECT NAME="company_type"> + <OPTION SELECTED>All <OPTION>Fuzzy <OPTION>Substring - <OPTION SELECTED>Exact + <OPTION>Exact </SELECT> <P><INPUT TYPE="submit" VALUE="Search"> diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi index 2b5b73a4b..2b1ab83b1 100755 --- a/httemplate/search/cust_main.cgi +++ b/httemplate/search/cust_main.cgi @@ -435,7 +435,7 @@ END my $pkg = $part_pkg->pkg; my $comment = $part_pkg->comment; - my $pkgview = $p. 'view/cust_pkg.cgi?'. $pkgnum; + my $pkgview = "${p}view/cust_main.cgi?$custnum#cust_pkg$pkgnum"; my @cust_svc = @{shift @lol_cust_svc}; #my(@cust_svc) = qsearch( 'cust_svc', { 'pkgnum' => $_->pkgnum } ); my $rowspan = scalar(@cust_svc) || 1; diff --git a/httemplate/search/cust_pay.cgi b/httemplate/search/cust_pay.cgi index 9eab5f82e..51dd3b340 100755 --- a/httemplate/search/cust_pay.cgi +++ b/httemplate/search/cust_pay.cgi @@ -42,7 +42,7 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq '_date' ) { } if ( $cgi->param('ending') && $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) { - my $ending = str2time($1) + 86400; + my $ending = str2time($1) + 86399; push @search, " _date <= $ending "; } my $search; diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi index 2e9dc5a22..9deaee591 100755 --- a/httemplate/search/cust_pkg.cgi +++ b/httemplate/search/cust_pkg.cgi @@ -29,7 +29,7 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) { } if ( $cgi->param('ending') && $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) { - my $ending = str2time($1) + 86400; + my $ending = str2time($1) + 86399; $range .= ( $range ? ' AND ' : ' WHERE ' ). " bill <= $ending "; } @@ -175,8 +175,8 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) { } if ( scalar(@cust_pkg) == 1 ) { - my($pkgnum)=$cust_pkg[0]->pkgnum; - print $cgi->redirect(popurl(2). "view/cust_pkg.cgi?$pkgnum"); + print $cgi->redirect("${p}view/cust_main.cgi?". $cust_pkg[0]->custnum. + "#cust_pkg". $cust_pkg[0]->pkgnum ); #exit; } elsif ( scalar(@cust_pkg) == 0 ) { #error %> @@ -300,7 +300,7 @@ END my $rowspan = scalar(@cust_svc) || 1; my $p = popurl(2); print $n1, <<END; - <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_pkg.cgi?$pkgnum"><FONT SIZE=-1>$pkgnum - $pkg</FONT></A></TD> + <TD ROWSPAN=$rowspan><A HREF="${p}view/cust_main.cgi?$custnum#cust_pkg$pkgnum"><FONT SIZE=-1>$pkgnum - $pkg</FONT></A></TD> <TD ROWSPAN=$rowspan>$setup</TD> END diff --git a/httemplate/search/report_cust_credit.html b/httemplate/search/report_cust_credit.html new file mode 100644 index 000000000..1b30685dc --- /dev/null +++ b/httemplate/search/report_cust_credit.html @@ -0,0 +1,54 @@ +<HTML> + <HEAD> + <TITLE>Payment report criteria</TITLE> + <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> + </HEAD> + <BODY BGCOLOR="#e8e8e8"> + <H1>Payment report criteria</H1> + <FORM ACTION="cust_pay.cgi" METHOD="post"> + <INPUT TYPE="hidden" NAME="magic" VALUE="_date"> + <TABLE> + <TR> + <TD ALIGN="right">Payments of type: </TD> + <TD><SELECT NAME="payby"> + <OPTION VALUE="">all</OPTION> + <OPTION VALUE="CARD">credit card (all)</OPTION> + <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION> + <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION> + <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION> + <OPTION VALUE="CHEK">electronic check / ACH</OPTION> + <OPTION VALUE="BILL">check / cash</OPTION> + </SELECT> + </TD> + </TR> + <TR> + <TD ALIGN="right">From: </TD> + <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD> +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "beginning_text", + ifFormat: "%m/%d/%Y", + button: "beginning_button", + align: "BR" + }); +</SCRIPT> + </TR> + <TD ALIGN="right">To: </TD> + <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD> +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "ending_text", + ifFormat: "%m/%d/%Y", + button: "ending_button", + align: "BR" + }); +</SCRIPT> + </TR> + </TABLE> + <BR><INPUT TYPE="submit" VALUE="Get Report"> + </FORM> + </BODY> +</HTML> diff --git a/httemplate/search/report_prepaid_income.cgi b/httemplate/search/report_prepaid_income.cgi new file mode 100644 index 000000000..1677591a3 --- /dev/null +++ b/httemplate/search/report_prepaid_income.cgi @@ -0,0 +1,86 @@ +<!-- mason kludge --> +<% + + #doesn't yet deal with daily/weekly packages + + #needs to be re-written in sql for efficiency + + my $time = time; + + my $now = $cgi->param('date') && str2time($cgi->param('date')) || $time; + $now =~ /^(\d+)$/ or die "unparsable date?"; + $now = $1; + + my( $total, $total_legacy ) = ( 0, 0 ); + + my @cust_bill_pkg = + grep { $_->cust_pkg && $_->cust_pkg->part_pkg->freq !~ /^([01]|\d+[dw])$/ } + qsearch( 'cust_bill_pkg', { + 'recur' => { op=>'!=', value=>0 }, + 'edate' => { op=>'>', value=>$now }, + }, ); + + my @cust_pkg = + grep { $_->part_pkg->recur != 0 + && $_->part_pkg->freq !~ /^([01]|\d+[dw])$/ + } + qsearch ( 'cust_pkg', { + 'bill' => { op=>'>', value=>$now } + } ); + + foreach my $cust_bill_pkg ( @cust_bill_pkg) { + my $period = $cust_bill_pkg->edate - $cust_bill_pkg->sdate; + + my $elapsed = $now - $cust_bill_pkg->sdate; + $elapsed = 0 if $elapsed < 0; + + my $remaining = 1 - $elapsed/$period; + + my $unearned = $remaining * $cust_bill_pkg->recur; + $total += $unearned; + + } + + foreach my $cust_pkg ( @cust_pkg ) { + my $period = $cust_pkg->bill - $cust_pkg->last_bill; + + my $elapsed = $now - $cust_pkg->last_bill; + $elapsed = 0 if $elapsed < 0; + + my $remaining = 1 - $elapsed/$period; + + my $unearned = $remaining * $cust_pkg->part_pkg->recur; #!! only works for flat/legacy + $total_legacy += $unearned; + + } + + $total = sprintf('%.2f', $total); + $total_legacy = sprintf('%.2f', $total_legacy); + +%> + +<%= header( 'Prepaid Income (Unearned Revenue) Report', + menubar( 'Main Menu'=>$p, ) ) %> +<%= table() %> + <TR> + <TH>Actual Unearned Revenue</TH> + <TH>Legacy Unearned Revenue</TH> + </TR> + <TR> + <TD ALIGN="right">$<%= $total %> + <TD ALIGN="right"> + <%= $now == $time ? "\$$total_legacy" : '<i>N/A</i>'%> + </TD> + </TR> + +</TABLE> +<BR> +Actual unearned revenue is the amount of unearned revenue Freeside has +actually invoiced for packages with longer-than monthly terms. +<BR><BR> +Legacy unearned revenue is the amount of unearned revenue represented by +customer packages. This number may be larger than actual unearned +revenue if you have imported longer-than monthly customer packages from +a previous billing system. +</BODY> +</HTML> diff --git a/httemplate/search/report_prepaid_income.html b/httemplate/search/report_prepaid_income.html new file mode 100644 index 000000000..e8b6ac4b1 --- /dev/null +++ b/httemplate/search/report_prepaid_income.html @@ -0,0 +1,39 @@ +<HTML> + <HEAD> + <TITLE>Prepaid Income (Unearned Revenue) Report</TITLE> + <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> + <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> + </HEAD> + <BODY BGCOLOR="#e8e8e8"> + <H1>Prepaid Income (Unearned Revenue) Report</H1> + <FORM ACTION="report_prepaid_income.cgi" METHOD="post"> + <TABLE> + <TR> + <TD>Prepaid income (unearned revenue) as of </TD> + <TD> + <INPUT TYPE="text" NAME="date" ID="date_text" VALUE="now"> + <IMG SRC="../images/calendar.png" ID="date_button" STYLE="cursor: pointer" TITLE="Select date"> + </TD> + </TR> + <TR> + <TD> + </TD> + <TD><i>m/d/y</i></TD> + </TR> + </TABLE> +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "date_text", + ifFormat: "%m/%d/%Y", + button: "date_button", + align: "BR" + }); +</SCRIPT> + + <INPUT TYPE="submit" VALUE="Generate report"> + </BODY> +</HTML> + <TABLE> + diff --git a/httemplate/search/report_receivables.cgi b/httemplate/search/report_receivables.cgi index ad353a1b3..0e95ad73c 100755 --- a/httemplate/search/report_receivables.cgi +++ b/httemplate/search/report_receivables.cgi @@ -93,7 +93,7 @@ where 0 < ,0 ) -order by lower(company), lower(last) +order by coalesce(lower(company), ''), lower(last) END diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi index 835554a2e..587665740 100755 --- a/httemplate/search/report_tax.cgi +++ b/httemplate/search/report_tax.cgi @@ -1,26 +1,253 @@ <!-- mason kludge --> <% -#my $user = getotaker; -my $user = $FS::UID::user; #dumb 1.4 8-char workaround +my $user = getotaker; $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/; -my $beginning = $1; +my $pbeginning = $1; +my $beginning = $1 ? str2time($1) : 0; $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/; -my $ending = $1; +my $pending = $1; +my $ending = ( $1 ? str2time($1) : 4294880896 ) + 86399; -print header('Tax Report Results'); +my $from_join_cust = " + FROM cust_bill_pkg + JOIN cust_bill USING ( invnum ) + JOIN cust_main USING ( custnum ) +"; +my $join_pkg = " + JOIN cust_pkg USING ( pkgnum ) + JOIN part_pkg USING ( pkgpart ) +"; +my $where = " + WHERE _date >= $beginning AND _date <= $ending + AND ( county = ? OR ? = '' ) + AND ( state = ? OR ? = '' ) + AND ( country = ? ) + AND payby != 'COMP' +"; +my @base_param = qw( county county state state country ); -open (REPORT, "freeside-tax-report -v -s $beginning -f $ending $user |"); +my $gotcust = " + WHERE 0 < ( SELECT COUNT(*) FROM cust_main + WHERE ( cust_main.county = cust_main_county.county + OR cust_main_county.county = '' + OR cust_main_county.county IS NULL ) + AND ( cust_main.state = cust_main_county.state + OR cust_main_county.state = '' + OR cust_main_county.state IS NULL ) + AND ( cust_main.country = cust_main_county.country ) + LIMIT 1 + ) +"; -print '<PRE>'; -while(<REPORT>) { - print $_; +my $monthly_exempt_warning = 0; +my($total, $exempt, $taxable, $owed, $tax) = ( 0, 0, 0, 0, 0 ); +my $out = 'Out of taxable region(s)'; +my %regions; +foreach my $r (qsearch('cust_main_county', {}, '', $gotcust) ) { + #warn $r->county. ' '. $r->state. ' '. $r->country. "\n"; + + my $label = getlabel($r); + $regions{$label}->{'label'} = $label; + + my $fromwhere = $from_join_cust. $join_pkg. $where; + my @param = @base_param; + + if ( $r->taxclass ) { + $fromwhere .= " AND ( taxclass = ? ) "; + push @param, 'taxclass'; + } + + my $nottax = 'pkgnum != 0'; + + my $a = scalar_sql($r, \@param, + "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) $fromwhere AND $nottax" + ); + $total += $a; + $regions{$label}->{'total'} += $a; + + foreach my $e ( grep { $r->get($_.'tax') =~ /^Y/i } + qw( cust_bill_pkg.setup cust_bill_pkg.recur ) ) { + my $x = scalar_sql($r, \@param, + "SELECT SUM($e) $fromwhere AND $nottax" + ); + $exempt += $x; + $regions{$label}->{'exempt'} += $x; + } + + my($t, $x) = (0, 0); + foreach my $e ( grep { $r->get($_.'tax') !~ /^Y/i } + qw( cust_bill_pkg.setup cust_bill_pkg.recur ) ) { + $t += scalar_sql($r, \@param, + "SELECT SUM($e) $fromwhere AND $nottax AND ( tax != 'Y' OR tax IS NULL )" + ); + + $x += scalar_sql($r, \@param, + "SELECT SUM($e) $fromwhere AND $nottax AND tax = 'Y'" + ); + } + + my($sday,$smon,$syear) = (localtime($beginning) )[ 3, 4, 5 ]; + $monthly_exempt_warning=1 if $sday != 1 && $beginning; + $smon++; $syear+=1900; + + my $eending = ( $ending == 4294967295 ) ? time : $ending; + my($eday,$emon,$eyear) = (localtime($eending) )[ 3, 4, 5 ]; + $emon++; $eyear+=1900; + + my $monthly_exemption = scalar_sql($r, [ 'taxnum' ], + "SELECT SUM(amount) FROM cust_tax_exempt where taxnum = ? ". + " AND ( year > $syear OR ( year = $syear and month >= $smon ) )". + " AND ( year < $eyear OR ( year = $eyear and month <= $emon ) )" + ); + #warn $r->taxnum(). ": $monthly_exemption\n"; + if ( $monthly_exemption ) { + $t -= $monthly_exemption; + $x += $monthly_exemption; + } + + $taxable += $t; + $regions{$label}->{'taxable'} += $t; + + $exempt += $x; + $regions{$label}->{'exempt'} += $x; + + $owed += $t * ($r->tax/100); + $regions{$label}->{'owed'} += $t * ($r->tax/100); + + if ( defined($regions{$label}->{'rate'}) + && $regions{$label}->{'rate'} != $r->tax.'%' ) { + $regions{$label}->{'rate'} = 'variable'; + } else { + $regions{$label}->{'rate'} = $r->tax.'%'; + } + +} + +my $taxwhere = "$from_join_cust $where"; +my @taxparam = @base_param; + +#foreach my $label ( keys %regions ) { +foreach my $r ( + qsearch( 'cust_main_county', + {}, + 'DISTINCT ON (country, state, county, taxname) *', + $gotcust + ) +) { + + #warn join('-', map { $r->$_() } qw( country state county taxname ) )."\n"; + + my $label = getlabel($r); + + my $fromwhere = $join_pkg. $where; + my @param = @base_param; + + #match itemdesc if necessary! + my $named_tax = + $r->taxname + ? 'AND itemdesc = '. dbh->quote($r->taxname) + : "AND ( itemdesc IS NULL OR itemdesc = '' OR itemdesc = 'Tax' )"; + my $x = scalar_sql($r, \@taxparam, + "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) $taxwhere ". + "AND pkgnum = 0 $named_tax", + ); + $tax += $x; + $regions{$label}->{'tax'} += $x; + +} + +#ordering +my @regions = map $regions{$_}, + sort { ( ($a eq $out) cmp ($b eq $out) ) || ($b cmp $a) } + keys %regions; + +push @regions, { + 'label' => 'Total', + 'total' => $total, + 'exempt' => $exempt, + 'taxable' => $taxable, + 'rate' => '', + 'owed' => $owed, + 'tax' => $tax, +}; + +#-- + +sub getlabel { + my $r = shift; + + my $label; + if ( $r->tax == 0 ) { + #kludge to avoid "will not stay shared" warning + my $out = 'Out of taxable region(s)'; + $label = $out; + } elsif ( $r->taxname ) { + $label = $r->taxname; +# $regions{$label}->{'taxname'} = $label; +# push @{$regions{$label}->{$_}}, $r->$_() foreach qw( county state country ); + } else { + $label = $r->country; + $label = $r->state.", $label" if $r->state; + $label = $r->county." county, $label" if $r->county; + #$label = $r->taxname. " ($label)" if $r->taxname; + } + return $label; } -print '</PRE>'; -print '</BODY></HTML>'; +#false laziness w/FS::Report::Table::Monthly (sub should probably be moved up +#to FS::Report or FS::Record or who the fuck knows where) +sub scalar_sql { + my( $r, $param, $sql ) = @_; + #warn "$sql\n"; + my $sth = dbh->prepare($sql) or die dbh->errstr; + $sth->execute( map $r->$_(), @$param ) + or die "Unexpected error executing statement $sql: ". $sth->errstr; + $sth->fetchrow_arrayref->[0] || 0; +} %> +<%= header( "Sales Tax Report - $pbeginning through ".($pending||'now'), + menubar( 'Main Menu'=>$p, ) ) %> +<%= table() %> + <TR> + <TH ROWSPAN=2></TH> + <TH COLSPAN=3>Sales</TH> + <TH ROWSPAN=2>Rate</TH> + <TH ROWSPAN=2>Tax owed</TH> + <TH ROWSPAN=2>Tax invoiced</TH> + </TR> + <TR> + <TH>Total</TH> + <TH>Non-taxable</TH> + <TH>Taxable</TH> + </TR> + <% foreach my $region ( @regions ) { %> + <TR> + <TD><%= $region->{'label'} %></TD> + <TD ALIGN="right">$<%= sprintf('%.2f', $region->{'total'} ) %></TD> + <TD ALIGN="right">$<%= sprintf('%.2f', $region->{'exempt'} ) %></TD> + <TD ALIGN="right">$<%= sprintf('%.2f', $region->{'taxable'} ) %></TD> + <TD ALIGN="right"><%= $region->{'rate'} %></TD> + <TD ALIGN="right">$<%= sprintf('%.2f', $region->{'owed'} ) %></TD> + <TD ALIGN="right">$<%= sprintf('%.2f', $region->{'tax'} ) %></TD> + </TR> + <% } %> + +</TABLE> + +<% if ( $monthly_exempt_warning ) { %> + <BR> + Partial-month tax reports (except for current month) may not be correct due + to month-granularity tax exemption (usually "texas tax"). For an accurate + report, start on the first of a month and end on the last day of a month (or + leave blank for to now). +<% } %> + +</BODY> +</HTML> + + diff --git a/httemplate/search/svc_acct.cgi b/httemplate/search/svc_acct.cgi index 044001bd2..29e851d91 100755 --- a/httemplate/search/svc_acct.cgi +++ b/httemplate/search/svc_acct.cgi @@ -48,13 +48,19 @@ if ( $query eq 'svcnum' ) { $orderby = "ORDER BY ${tblname}username"; } elsif ( $query eq 'uid' ) { $sortby=\*uid_sort; - $orderby = ( $unlinked ? 'AND' : 'WHERE' ). + $orderby = ( $unlinked ? ' AND' : ' WHERE' ). " ${tblname}uid IS NOT NULL ORDER BY ${tblname}uid"; } elsif ( $cgi->param('popnum') =~ /^(\d+)$/ ) { $unlinked .= ( $unlinked ? 'AND' : 'WHERE' ). " popnum = $1"; $sortby=\*username_sort; $orderby = "ORDER BY ${tblname}username"; +} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) { + $unlinked .= ( $unlinked ? ' AND' : ' WHERE' ). + " $1 = ( SELECT svcpart FROM cust_svc ". + " WHERE cust_svc.svcnum = svc_acct.svcnum ) "; + $sortby=\*uid_sort; + #$sortby=\*svcnum_sort; } else { $sortby=\*uid_sort; @svc_acct = @{&usernamesearch}; @@ -65,6 +71,7 @@ if ( $query eq 'svcnum' || $query eq 'username' || $query eq 'uid' || $cgi->param('popnum') =~ /^(\d+)$/ + || $cgi->param('svcpart') =~ /^(\d+)$/ ) { my $statement = "SELECT COUNT(*) FROM svc_acct $unlinked"; diff --git a/httemplate/search/svc_domain.cgi b/httemplate/search/svc_domain.cgi index fbdecc118..cd78dc128 100755 --- a/httemplate/search/svc_domain.cgi +++ b/httemplate/search/svc_domain.cgi @@ -24,6 +24,13 @@ if ( $query eq 'svcnum' ) { 'svcnum' => $_->svcnum, 'pkgnum' => '', }), qsearch('svc_domain',{}); +} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) { + @svc_domain = + qsearch( 'svc_domain', {}, '', + " WHERE $1 = ( SELECT svcpart FROM cust_svc ". + " WHERE cust_svc.svcnum = svc_domain.svcnum ) " + ); + $sortby=\*svcnum_sort; } else { $cgi->param('domain') =~ /^([\w\-\.]+)$/; my($domain)=$1; diff --git a/httemplate/search/svc_forward.cgi b/httemplate/search/svc_forward.cgi new file mode 100755 index 000000000..10094bc99 --- /dev/null +++ b/httemplate/search/svc_forward.cgi @@ -0,0 +1,79 @@ +<% + +my $conf = new FS::Conf; + +my($query)=$cgi->keywords; +$query ||= ''; #to avoid use of unitialized value errors +my(@svc_forward,$sortby); +if ( $query eq 'svcnum' ) { + $sortby=\*svcnum_sort; + @svc_forward=qsearch('svc_forward',{}); +} else { + eidiot('unimplemented'); +} + +if ( scalar(@svc_forward) == 1 ) { + print $cgi->redirect(popurl(2). "view/svc_forward.cgi?". $svc_forward[0]->svcnum); + #exit; +} elsif ( scalar(@svc_forward) == 0 ) { +%> +<!-- mason kludge --> +<% + eidiot "No matching forwards found!\n"; +} else { +%> +<!-- mason kludge --> +<% + my $total = scalar(@svc_forward); + print header("Mail forward Search Results",''), <<END; + + $total matching mail forwards found + <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0> + <TR> + <TH>Service #<BR><FONT SIZE=-1>(click to view forward)</FONT></TH> + <TH>Mail to<BR><FONT SIZE=-1>(click to view account)</FONT></TH> + <TH>Forwards to<BR><FONT SIZE=-1>(click to view account)</FONT></TH> + </TR> +END + + foreach my $svc_forward ( + sort $sortby (@svc_forward) + ) { + my $svcnum = $svc_forward->svcnum; + + my $src = $svc_forward->src; + $src = "<I>(anything)</I>$src" if $src =~ /^@/; + if ( $svc_forward->srcsvc_acct ) { + $src = qq!<A HREF="${p}view/svc_acct.cgi?!. $svc_forward->srcsvc. '">'. + $svc_forward->srcsvc_acct->email. '</A>'; + } + + my $dst = $svc_forward->dst; + if ( $svc_forward->dstsvc_acct ) { + $dst = qq!<A HREF="${p}view/svc_acct.cgi?!. $svc_forward->dstsvc. '">'. + $svc_forward->dstsvc_acct->email. '</A>'; + } + + print <<END; + <TR> + <TD><A HREF="${p}view/svc_forward.cgi?$svcnum">$svcnum</A></TD> + <TD>$src</TD> + <TD>$dst</TD> + </TR> +END + + } + + print <<END; + </TABLE> + </BODY> +</HTML> +END + +} + +sub svcnum_sort { + $a->getfield('svcnum') <=> $b->getfield('svcnum'); +} + +%> diff --git a/httemplate/view/cust_bill-pdf.cgi b/httemplate/view/cust_bill-pdf.cgi new file mode 100755 index 000000000..2a86c3246 --- /dev/null +++ b/httemplate/view/cust_bill-pdf.cgi @@ -0,0 +1,17 @@ +<% + +#untaint invnum +my($query) = $cgi->keywords; +$query =~ /^(\d+)(.pdf)?$/; +my $invnum = $1; + +my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); +die "Invoice #$invnum not found!" unless $cust_bill; + +my $pdf = $cust_bill->print_pdf; + +http_header('Content-Type' => 'application/pdf' ); +http_header('Content-Length' => length($pdf) ); +http_header('Cache-control' => 'max-age=60' ); +%> +<%= $pdf %> diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi index ddc92748d..7906af65b 100755 --- a/httemplate/view/cust_bill.cgi +++ b/httemplate/view/cust_bill.cgi @@ -20,11 +20,20 @@ print header('Invoice View', menubar( print qq!<A HREF="${p}edit/cust_pay.cgi?$invnum">Enter payments (check/cash) against this invoice</A> | ! if $cust_bill->owed > 0; -print qq!<A HREF="${p}misc/print-invoice.cgi?$invnum">Reprint this invoice</A>!. '<BR><BR>'; +print qq!<A HREF="${p}misc/print-invoice.cgi?$invnum">Reprint this invoice</A>!; +if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) { + print qq! | <A HREF="${p}misc/email-invoice.cgi?$invnum">!. + qq!Re-email this invoice</A>!; +} + +print '<BR><BR>'; -print menubar( - 'View typeset invoice' => "${p}view/cust_bill-ps.cgi?$invnum", -), '<BR><BR>'; +my $conf = new FS::Conf; +if ( $conf->exists('invoice_latex') ) { + print menubar( + 'View typeset invoice' => "${p}view/cust_bill-pdf.cgi?$invnum.pdf", + ), '<BR><BR>'; +} #false laziness with search/cust_bill_event.cgi diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index ee5f86973..bbf34db11 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -3,7 +3,6 @@ my $conf = new FS::Conf; -#false laziness with view/cust_pkg.cgi, but i'm trying to make that go away so my %uiview = (); my %uiadd = (); foreach my $part_svc ( qsearch('part_svc',{}) ) { @@ -15,14 +14,15 @@ print header("Customer View", menubar( 'Main Menu' => popurl(2) )); -print <<END; +%> + <STYLE TYPE="text/css"> .package TH { font-size: medium } .package TR { font-size: smaller } -.package .pkgnum { font-size: medium } .package .provision { font-weight: bold } </STYLE> -END + +<% die "No customer specified (bad URL)!" unless $cgi->keywords; my($query) = $cgi->keywords; # needs parens with my, ->keywords returns array @@ -33,14 +33,16 @@ die "Customer not found!" unless $cust_main; print qq!<A HREF="${p}edit/cust_main.cgi?$custnum">Edit this customer</A>!; -print <<END; +%> + <SCRIPT> function cancel_areyousure(href) { if (confirm("Perminantly delete all services and cancel this customer?") == true) window.location.href = href; } </SCRIPT> -END + +<% print qq! | <A HREF="javascript:cancel_areyousure('${p}misc/cust_main-cancel.cgi?$custnum')">!. 'Cancel this customer</A>' @@ -299,13 +301,25 @@ if ( defined $cust_main->dbdef_table->column('comments') '</PRE></TD></TR></TABLE></TABLE>'; } -print '</TD></TR></TABLE>'; +%> + +</TD></TR></TABLE> -print '<BR>'. - '<FORM ACTION="'.popurl(2).'edit/process/quick-cust_pkg.cgi" METHOD="POST">'. - qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!. - '<SELECT NAME="pkgpart"><OPTION> '; +<BR> +<SCRIPT TYPE="text/javascript"> +function enable_order_pkg () { + if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) { + document.OrderPkgForm.submit.disabled = false; + } else { + document.OrderPkgForm.submit.disabled = true; + } +} +</SCRIPT> +<FORM NAME="OrderPkgForm" ACTION="<%= $p %>edit/process/quick-cust_pkg.cgi" METHOD="POST"> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>"> +<SELECT NAME="pkgpart" onChange="enable_order_pkg()"><OPTION>Order additional package +<% foreach my $part_pkg ( qsearch( 'part_pkg', { 'disabled' => '' }, '', ' AND 0 < ( SELECT COUNT(*) FROM type_pkgs '. @@ -313,15 +327,17 @@ foreach my $part_pkg ( ' AND type_pkgs.pkgpart = part_pkg.pkgpart )' ) ) { - print '<OPTION VALUE="'. $part_pkg->pkgpart. '">'. $part_pkg->pkg. ' - '. - $part_pkg->comment; -} +%> +<OPTION VALUE="<%= $part_pkg->pkgpart %>"><%= $part_pkg->pkg %> - <%= $part_pkg->comment %> +<% } %> + +</SELECT><INPUT NAME="submit" TYPE="submit" VALUE="Order Package" disabled></FORM><BR> -print '</SELECT><INPUT TYPE="submit" VALUE="Order Package"></FORM><BR>'; +<% if ( $conf->config('payby-default') ne 'HIDE' ) { - print '<BR>'. + print qq!<FORM ACTION="${p}edit/process/quick-charge.cgi" METHOD="POST">!. qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!. qq!Description:<INPUT TYPE="text" NAME="pkg">!. @@ -348,7 +364,8 @@ if ( $conf->config('payby-default') ne 'HIDE' ) { } -print <<END; +%> + <SCRIPT> function cust_pkg_areyousure(href) { if (confirm("Permanently delete included services and cancel this package?") == true) @@ -359,10 +376,10 @@ function svc_areyousure(href) { window.location.href = href; } </SCRIPT> -END -print qq!<BR><A NAME="cust_pkg">Packages</A> !, -# qq!<BR>Click on package number to view/edit package.!, +<% + +print qq!<A NAME="cust_pkg">Packages</A> !, qq!( <A HREF="!, popurl(2), qq!edit/cust_pkg.cgi?$custnum">Order and cancel packages</A> (preserves services) )!, ; @@ -376,7 +393,7 @@ if ( @$packages ) { %> <TABLE CLASS="package" BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999"> <TR> - <TH COLSPAN=2>Package</TH> + <TH>Package</TH> <TH>Status</TH> <TH COLSPAN=2>Services</TH> </TR> @@ -396,9 +413,9 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { %> <!--pkgnum: <%=$pkg->{pkgnum}%>--> <TR> - <TD ROWSPAN=<%=$rowspan%> CLASS="pkgnum"><%=$pkg->{pkgnum}%></TD> <TD ROWSPAN=<%=$rowspan%>> - <%=$pkg->{pkg}%> - <%=$pkg->{comment}%> ( <%=pkg_details_link($pkg)%> )<BR> + <A NAME="cust_pkg<%=$pkg->{pkgnum}%>"><%=$pkg->{pkgnum}%></A>: + <%=$pkg->{pkg}%> - <%=$pkg->{comment}%><BR> <% unless ($pkg->{cancel}) { %> ( <%=pkg_change_link($pkg)%> ) ( <%=pkg_dates_link($pkg)%> | <%=pkg_customize_link($pkg,$custnum)%> ) @@ -532,17 +549,16 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { } if ($svcpart->{count} < $svcpart->{quantity}) { print qq!<TR>\n! if ($cnt > 0); - print qq! <TD COLSPAN=2>!.svc_provision_link($pkg,$svcpart).qq!</TD>\n</TR>\n!; + print qq! <TD COLSPAN=2>!.svc_provision_link($pkg, $svcpart, $conf).qq!</TD>\n</TR>\n!; } } } -print '</TABLE>' +print '</TABLE>'; } #end display packages +%> - -print <<END; <SCRIPT> function cust_pay_areyousure(href) { if (confirm("Are you sure you want to delete this payment?") @@ -554,171 +570,216 @@ function cust_pay_unapply_areyousure(href) { == true) window.location.href = href; } +function cust_credit_unapply_areyousure(href) { + if (confirm("Are you sure you want to unapply this credit?") + == true) + window.location.href = href; +} function cust_credit_areyousure(href) { if (confirm("Are you sure you want to delete this credit?") == true) window.location.href = href; } </SCRIPT> -END -if ( $conf->config('payby-default') ne 'HIDE' ) { - - #formatting - print qq!<BR><BR><A NAME="history">Payment History!. - qq!</A> ( !. - qq!<A HREF="!. popurl(2). qq!edit/cust_pay.cgi?custnum=$custnum">!. - qq!Post payment</A> | !. - qq!<A HREF="!. popurl(2). qq!edit/cust_credit.cgi?$custnum">!. - qq!Post credit</A> )!; +<% if ( $conf->config('payby-default') ne 'HIDE' ) { %> + <BR><BR><A NAME="history">Payment History</A> + (<A HREF="<%= $p %>edit/cust_pay.cgi?custnum=<%= $custnum %>">Post payment</A> + | <A HREF="<%= $p %>edit/cust_credit.cgi?<%= $custnum %>">Post credit</A>) + + <% #get payment history - # - # major problem: this whole thing is way too sloppy. - # minor problem: the description lines need better formatting. - - my @history = (); #needed for mod_perl :) - - my %target = (); - - my @bills = qsearch('cust_bill',{'custnum'=>$custnum}); - foreach my $bill (@bills) { - my($bref)=$bill->hashref; - my $bpre = ( $bill->owed > 0 ) - ? '<b><font size="+1" color="#ff0000"> Open ' - : ''; - my $bpost = ( $bill->owed > 0 ) ? '</font></b>' : ''; - push @history, - $bref->{_date} . qq!\t<A HREF="!. popurl(2). qq!view/cust_bill.cgi?! . - $bref->{invnum} . qq!">${bpre}Invoice #! . $bref->{invnum} . - qq! (Balance \$! . $bill->owed . qq!)$bpost</A>\t! . - $bref->{charged} . qq!\t\t\t!; - - my(@cust_bill_pay)=qsearch('cust_bill_pay',{'invnum'=> $bref->{invnum} } ); - # my(@payments)=qsearch('cust_pay',{'invnum'=> $bref->{invnum} } ); - # my($payment); - foreach my $cust_bill_pay (@cust_bill_pay) { - my $payment = $cust_bill_pay->cust_pay; - my($date,$invnum,$payby,$payinfo,$paid)=($payment->_date, - $cust_bill_pay->invnum, - $payment->payby, - $payment->payinfo, - $cust_bill_pay->amount, - ); - $payinfo = 'x'x(length($payinfo)-4). substr($payinfo,(length($payinfo)-4)) - if $payby eq 'CARD'; - my $target = "$payby$payinfo"; - $payby =~ s/^BILL$/Check #/ if $payinfo; - $payby =~ s/^(CARD|COMP)$/$1 /; - my $delete = $payment->closed !~ /^Y/i && $conf->exists('deletepayments') - ? qq! (<A HREF="javascript:cust_pay_areyousure('${p}misc/delete-cust_pay.cgi?!. $payment->paynum. qq!')">delete</A>)! - : ''; - my $unapply = - $payment->closed !~ /^Y/i && $conf->exists('unapplypayments') - ? qq! (<A HREF="javascript:cust_pay_unapply_areyousure('${p}misc/unapply-cust_pay.cgi?!. $payment->paynum. qq!')">unapply</A>)! - : ''; - push @history, - "$date\tPayment, Invoice #$invnum ($payby$payinfo)$delete$unapply\t\t$paid\t\t\t$target"; + my @history = (); + + #invoices + foreach my $cust_bill ($cust_main->cust_bill) { + my $pre = ( $cust_bill->owed > 0 ) + ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open ' + : ''; + my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : ''; + my $invnum = $cust_bill->invnum; + push @history, { + 'date' => $cust_bill->_date, + 'desc' => qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!. $pre. + "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'. + $post. '</A>', + 'charge' => $cust_bill->charged, + }; + } + + #payments (some false laziness w/credits) + foreach my $cust_pay ($cust_main->cust_pay) { + + my $payby = $cust_pay->payby; + my $payinfo = $cust_pay->payinfo; + my @cust_bill_pay = $cust_pay->cust_bill_pay; + + $payinfo = 'x'x(length($payinfo)-4). substr($payinfo,(length($payinfo)-4)) + if $payby eq 'CARD'; + my $target = "$payby$payinfo"; + $payby =~ s/^BILL$/Check #/ if $payinfo; + $payby =~ s/^BILL$//; + $payby =~ s/^(CARD|COMP)$/$1 /; + my $info = $payby ? " ($payby$payinfo)" : ''; + + my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); + if ( scalar(@cust_bill_pay) == 0 ) { + #completely unapplied + $pre = '<B><FONT COLOR="#FF0000">Unapplied '; + $post = '</FONT></B>'; + $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. + $cust_pay->paynum. '">apply</A>)'; + } elsif ( scalar(@cust_bill_pay) == 1 && $cust_pay->unapplied == 0 ) { + #applied to one invoice + $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum; + } else { + #complicated + $desc = '<BR>'; + foreach my $cust_bill_pay (@cust_bill_pay) { + $desc .= ' '. + '$'. $cust_bill_pay->amount. + ' applied to Invoice #'. $cust_bill_pay->invnum. + '<BR>'; + #' on '. time2str("%D", $cust_bill_pay->_date). + + } + if ( $cust_pay->unapplied > 0 ) { + $desc .= ' '. + '<B><FONT COLOR="#FF0000">$'. + $cust_pay->unapplied. ' unapplied</FONT></B>'. + qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. + $cust_pay->paynum. '">apply</A>)'. + '<BR>'; + } } - - my(@cust_credit_bill)= - qsearch('cust_credit_bill', { 'invnum'=> $bref->{invnum} } ); - foreach my $cust_credit_bill (@cust_credit_bill) { - my $cust_credit = $cust_credit_bill->cust_credit; - my($date, $invnum, $crednum, $amount, $reason, $app_date ) = ( - $cust_credit->_date, - $cust_credit_bill->invnum, - $cust_credit_bill->crednum, - $cust_credit_bill->amount, - $cust_credit->reason, - time2str("%D", $cust_credit_bill->_date), - ); - my $delete = - $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') - ? qq! (<A HREF="javascript:cust_credit_areyousure('${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum. qq!')">delete</A>)! - : ''; - push @history, - "$date\tCredit #$crednum: $reason<BR>". - "(applied to invoice #$invnum on $app_date)$delete\t\t\t$amount\t"; + + my $delete = ''; + if ( $cust_pay->closed !~ /^Y/i && $conf->exists('deletepayments') ) { + $delete = qq! (<A HREF="javascript:cust_pay_areyousure('!. + qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum. + qq!')">delete</A>)!; } - } - - my @credits = grep { scalar(my @array = $_->cust_credit_refund) } - qsearch('cust_credit',{'custnum'=>$custnum}); - foreach my $credit (@credits) { - my($cref)=$credit->hashref; - my(@cust_credit_refund)= - qsearch('cust_credit_refund', { 'crednum'=> $cref->{crednum} } ); - foreach my $cust_credit_refund (@cust_credit_refund) { - my $cust_refund = $cust_credit_refund->cust_credit; - my($date, $crednum, $amount, $reason, $app_date ) = ( - $credit->_date, - $credit->crednum, - $cust_credit_refund->amount, - $credit->reason, - time2str("%D", $cust_credit_refund->_date), - ); - push @history, - "$date\tCredit #$crednum: $reason<BR>". - "(applied to refund on $app_date)\t\t\t$amount\t"; + + my $unapply = ''; + if ( $cust_pay->closed !~ /^Y/i + && $conf->exists('unapplypayments') + && scalar(@cust_bill_pay) ) { + $unapply = qq! (<A HREF="javascript:cust_pay_unapply_areyousure('!. + qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum. + qq!')">unapply</A>)!; } + + push @history, { + 'date' => $cust_pay->_date, + 'desc' => $pre. "Payment$post$info$desc". + "$apply$delete$unapply", + 'payment' => $cust_pay->paid, + 'target' => $target, + }; } - - @credits = grep { $_->credited > 0 } - qsearch('cust_credit',{'custnum'=>$custnum}); - foreach my $credit (@credits) { - my($cref)=$credit->hashref; - my $delete = - $credit->closed !~ /^Y/i && $conf->exists('deletecredits') - ? qq! (<A HREF="javascript:cust_credit_areyousure('${p}misc/delete-cust_credit.cgi?!. $credit->crednum. qq!')">delete</A>)! - : ''; - push @history, - $cref->{_date} . "\t" . - qq!<A HREF="! . popurl(2). qq!edit/cust_credit_bill.cgi?!. $cref->{crednum} . qq!">!. - '<b><font size="+1" color="#ff0000">Unapplied credit #' . - $cref->{crednum} . "</font></b></A>: ". - $cref->{reason} . "$delete\t\t\t" . $credit->credited . "\t"; - } - - my(@refunds)=qsearch('cust_refund',{'custnum'=> $custnum } ); - foreach my $refund (@refunds) { - my($rref)=$refund->hashref; - my($refundnum) = ( - $refund->refundnum, - ); - - push @history, - $rref->{_date} . "\tRefund #$refundnum, (" . - $rref->{payby} . " " . $rref->{payinfo} . ") by " . - $rref->{otaker} . " - ". $rref->{reason} . "\t\t\t\t" . - $rref->{refund}; + + #credits (some false laziness w/payments) + foreach my $cust_credit ($cust_main->cust_credit) { + + my @cust_credit_bill = $cust_credit->cust_credit_bill; + my @cust_credit_refund = $cust_credit->cust_credit_refund; + + my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); + if ( scalar(@cust_credit_bill) == 0 + && scalar(@cust_credit_refund) == 0 ) { + #completely unapplied + $pre = '<B><FONT COLOR="#FF0000">Unapplied '; + $post = '</FONT></B>'; + $apply = qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. + $cust_credit->crednum. '">apply</A>)'; + } elsif ( scalar(@cust_credit_bill) == 1 + && scalar(@cust_credit_refund) == 0 + && $cust_credit->credited == 0 ) { + #applied to one invoice + $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum; + } elsif ( scalar(@cust_credit_bill) == 0 + && scalar(@cust_credit_refund) == 1 + && $cust_credit->credited == 0 ) { + #applied to one refund + $desc = ' refunded on '. time2str("%D", $cust_credit_refund[0]->_date); + } else { + #complicated + $desc = '<BR>'; + foreach my $app ( sort { $a->_date <=> $b->_date } + ( @cust_credit_bill, @cust_credit_refund ) ) { + if ( $app->isa('FS::cust_credit_bill') ) { + $desc .= ' '. + '$'. $app->amount. + ' applied to Invoice #'. $app->invnum. + '<BR>'; + #' on '. time2str("%D", $app->_date). + } elsif ( $app->isa('FS::cust_credit_refund') ) { + $desc .= ' '. + '$'. $app->amount. + ' refunded on'. time2str("%D", $app->_date). + '<BR>'; + } else { + die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund"; + } + } + if ( $cust_credit->credited > 0 ) { + $desc .= ' <B><FONT COLOR="#FF0000">$'. + $cust_credit->credited. ' unapplied</FONT></B>'. + qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. + $cust_credit->crednum. '">apply</A>)'. + '<BR>'; + } + } +# + my $delete = ''; + if ( $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') ) { + $delete = qq! (<A HREF="javascript:cust_credit_areyousure('!. + qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum. + qq!')">delete</A>)!; + } + + my $unapply = ''; + if ( $cust_credit->closed !~ /^Y/i + && $conf->exists('unapplycredits') + && scalar(@cust_credit_bill) ) { + $unapply = qq! (<A HREF="javascript:cust_credit_unapply_areyousure('!. + qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum. + qq!')">unapply</A>)!; + } + + push @history, { + 'date' => $cust_credit->_date, + 'desc' => $pre. "Credit$post by ". $cust_credit->otaker. + ' ('. $cust_credit->reason. ')'. + "$desc$apply$delete$unapply", + 'credit' => $cust_credit->amount, + }; + } - - my @unapplied_payments = - grep { $_->unapplied > 0 } qsearch('cust_pay', { 'custnum' => $custnum } ); - foreach my $payment (@unapplied_payments) { - my $payby = $payment->payby; - my $payinfo = $payment->payinfo; - #false laziness w/above + + #refunds + foreach my $cust_refund ($cust_main->cust_refund) { + + my $payby = $cust_refund->payby; + my $payinfo = $cust_refund->payinfo; + $payinfo = 'x'x(length($payinfo)-4). substr($payinfo,(length($payinfo)-4)) if $payby eq 'CARD'; - my $target = "$payby$payinfo"; $payby =~ s/^BILL$/Check #/ if $payinfo; $payby =~ s/^(CARD|COMP)$/$1 /; - my $delete = $payment->closed !~ /^Y/i && $conf->exists('deletepayments') - ? qq! (<A HREF="javascript:cust_pay_areyousure('${p}misc/delete-cust_pay.cgi?!. $payment->paynum. qq!')">delete</A>)! - : ''; - push @history, - $payment->_date. "\t". - '<b><font size="+1" color="#ff0000">Unapplied payment #' . - $payment->paynum . " ($payby$payinfo)</font></b> ". - '(<A HREF="'. popurl(2). 'edit/cust_bill_pay.cgi?'. $payment->paynum. '">'. - "apply</A>)$delete". - "\t\t" . $payment->unapplied . "\t\t\t$target"; + + push @history, { + 'date' => $cust_refund->_date, + 'desc' => "Refund ($payby$payinfo) by ". $cust_refund->otaker, + 'refund' => $cust_refund->refund, + }; + } - #formatting - print &table(), <<END; + %> + + <%= table() %> <TR> <TH>Date</TH> <TH>Description</TH> @@ -728,59 +789,68 @@ if ( $conf->config('payby-default') ne 'HIDE' ) { <TH><FONT SIZE=-1>Refund</FONT></TH> <TH><FONT SIZE=-1>Balance</FONT></TH> </TR> -END - + + <% #display payment history - + + my %target; my $balance = 0; - foreach my $item (sort keyfield_numerically @history) { - my($date,$desc,$charge,$payment,$credit,$refund,$target)=split(/\t/,$item); - $charge ||= 0; - $payment ||= 0; - $credit ||= 0; - $refund ||= 0; - $balance += $charge - $payment; - $balance -= $credit - $refund; + foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) { + + my $charge = exists($item->{'charge'}) + ? sprintf('$%.2f', $item->{'charge'}) + : ''; + my $payment = exists($item->{'payment'}) + ? sprintf('- $%.2f', $item->{'payment'}) + : ''; + my $credit = exists($item->{'credit'}) + ? sprintf('- $%.2f', $item->{'credit'}) + : ''; + my $refund = exists($item->{'refund'}) + ? sprintf('$%.2f', $item->{'refund'}) + : ''; + + my $target = exists($item->{'target'}) ? $item->{'target'} : ''; + + $balance += $item->{'charge'} if exists $item->{'charge'}; + $balance -= $item->{'payment'} if exists $item->{'payment'}; + $balance -= $item->{'credit'} if exists $item->{'credit'}; + $balance += $item->{'refund'} if exists $item->{'refund'}; $balance = sprintf("%.2f", $balance); $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp - $target = '' unless defined $target; + ( my $showbalance = '$'. $balance ) =~ s/^\$\-/- \$/; + + %> - print "<TR><TD><FONT SIZE=-1>"; - print qq!<A NAME="$target">! unless $target && $target{$target}++; - print time2str("%D",$date); - print '</A>' if $target && $target{$target} == 1; - print "</FONT></TD>", - "<TD><FONT SIZE=-1>$desc</FONT></TD>", - "<TD><FONT SIZE=-1>", - ( $charge ? "\$".sprintf("%.2f",$charge) : '' ), - "</FONT></TD>", - "<TD><FONT SIZE=-1>", - ( $payment ? "- \$".sprintf("%.2f",$payment) : '' ), - "</FONT></TD>", - "<TD><FONT SIZE=-1>", - ( $credit ? "- \$".sprintf("%.2f",$credit) : '' ), - "</FONT></TD>", - "<TD><FONT SIZE=-1>", - ( $refund ? "\$".sprintf("%.2f",$refund) : '' ), - "</FONT></TD>", - "<TD><FONT SIZE=-1>\$" . $balance, - "</FONT></TD>", - "\n"; - } + <TR> + <TD> + <% unless ( !$target || $target{$target}++ ) { %> + <A NAME="<%= $target %>"> + <% } %> + <%= time2str("%D",$item->{'date'}) %> + <% if ( $target && $target{$target} == 1 ) { %> + </A> + <% } %> + </FONT> + </TD> + <TD><%= $item->{'desc'} %></TD> + <TD ALIGN="right"><%= $charge %></TD> + <TD ALIGN="right"><%= $payment %></TD> + <TD ALIGN="right"><%= $credit %></TD> + <TD ALIGN="right"><%= $refund %></TD> + <TD ALIGN="right"><%= $showbalance %></TD> + </TR> + + <% } %> - print "</TABLE>"; - -} + </TABLE> -print '</BODY></HTML>'; - -#subroutiens -sub keyfield_numerically { (split(/\t/,$a))[0] <=> (split(/\t/,$b))[0]; } +<% } %> -%> +</BODY></HTML> <% - +#subroutines sub get_packages { my $cust_main = shift or return undef; @@ -840,7 +910,7 @@ sub get_packages { ) ) { - warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n"; + #warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n"; my $svc = { 'svcnum' => $cust_svc->svcnum, 'label' => ($cust_svc->label)[1], @@ -888,13 +958,21 @@ sub svc_label_link { } sub svc_provision_link { - my ($pkg, $svcpart) = (shift,shift) or return ''; + my ($pkg, $svcpart, $conf) = @_; ( my $svc_nbsp = $svcpart->{svc} ) =~ s/\s+/ /g; - return qq!<A CLASS="provision" HREF="${p}edit/$svcpart->{svcdb}.cgi?! . - qq!pkgnum$pkg->{pkgnum}-svcpart$svcpart->{svcpart}">! . - "Provision $svc_nbsp (". - ($svcpart->{quantity} - $svcpart->{count}). - ')</A>'; + my $pkgnum_svcpart = "pkgnum$pkg->{pkgnum}-svcpart$svcpart->{svcpart}"; + my $num_left = $svcpart->{quantity} - $svcpart->{count}; + + my $link = qq!<A CLASS="provision" HREF="${p}edit/$svcpart->{svcdb}.cgi?!. + qq!$pkgnum_svcpart">!. + "Provision $svc_nbsp ($num_left)</A>"; + if ( $conf->exists('legacy_link') ) { + $link .= '<BR>'. + qq!<A CLASS="provision" HREF="${p}misc/link.cgi?!. + qq!$pkgnum_svcpart">!. + "Link to legacy $svc_nbsp ($num_left)</A>"; + } + $link; } sub svc_unprovision_link { @@ -925,11 +1003,6 @@ sub pkg_datestr { $strip; } -sub pkg_details_link { - my $pkg = shift or return ''; - return qq!<a href="${p}view/cust_pkg.cgi?$pkg->{pkgnum}">Details</a>!; -} - sub pkg_change_link { my $pkg = shift or return ''; return qq!<a href="${p}misc/change_pkg.cgi?$pkg->{pkgnum}">Change package</a>!; @@ -947,7 +1020,8 @@ sub pkg_unsuspend_link { sub pkg_cancel_link { my $pkg = shift or return ''; - return qq!<A HREF="javascript:cust_pkg_areyousure('${p}misc/cancel_pkg.cgi?$pkg->{pkgnum}')">Cancel</A>!; + qq!<A HREF="javascript:cust_pkg_areyousure('${p}misc/cancel_pkg.cgi?$pkg->{pkgnum}')">Cancel now</A> | !. + qq!<A HREF="${p}misc/expire_pkg.cgi?$pkg->{pkgnum}">Cancel later</A>!; } sub pkg_dates_link { diff --git a/httemplate/view/svc_acct.cgi b/httemplate/view/svc_acct.cgi index f92e4e240..8657f16ab 100755 --- a/httemplate/view/svc_acct.cgi +++ b/httemplate/view/svc_acct.cgi @@ -50,8 +50,7 @@ function areyousure(href) { <%= header('Account View', menubar( ( ( $pkgnum || $custnum ) - ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum", - "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + ? ( "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", ) : ( "Cancel this (unaudited) account" => "javascript:areyousure(\'${p}misc/cancel-unaudited.cgi?$svcnum\')" ) diff --git a/httemplate/view/svc_domain.cgi b/httemplate/view/svc_domain.cgi index fd017de46..cd9f79d36 100755 --- a/httemplate/view/svc_domain.cgi +++ b/httemplate/view/svc_domain.cgi @@ -34,8 +34,7 @@ my $domain = $svc_domain->domain; <%= header('Domain View', menubar( ( ( $pkgnum || $custnum ) - ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum", - "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + ? ( "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", ) : ( "Cancel this (unaudited) domain" => "${p}misc/cancel-unaudited.cgi?$svcnum" ) @@ -48,13 +47,16 @@ Service #<%= $svcnum %> <BR>Domain name: <B><%= $domain %></B> <BR>Catch all email <A HREF="<%= ${p} %>misc/catchall.cgi?<%= $svcnum %>">(change)</A>: <%= $email ? "<B>$email</B>" : "<I>(none)<I>" %> -<BR><BR><A HREF="http://www.geektools.com/cgi-bin/proxy.cgi?query=<%=$domain%>;targetnic=auto">View whois information.</A> +<BR><BR><A HREF="<%= ${p} %>misc/whois.cgi?custnum=<%=$custnum%>;svcnum=<%=$svcnum%>;domain=<%=$domain%>">View whois information.</A> <BR><BR> <SCRIPT> function areyousure(href) { if ( confirm("Remove this record?") == true ) window.location.href = href; } + function slave_areyousure() { + return confirm("Remove all records and slave from " + document.SlaveForm.recdata.value + "?"); + } </SCRIPT> <% my @records; if ( @records = $svc_domain->domain_record ) { %> @@ -91,7 +93,7 @@ Service #<%= $svcnum %> </SELECT> <INPUT TYPE="text" NAME="recdata"> <INPUT TYPE="submit" VALUE="Add record"> </FORM><BR><BR>or<BR><BR> -<FORM METHOD="POST" ACTION="<%=$p%>edit/process/domain_record.cgi"> +<FORM NAME="SlaveForm" METHOD="POST" ACTION="<%=$p%>edit/process/domain_record.cgi"> <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%=$svcnum%>"> <% if ( @records ) { %> Delete all records and <% } %> @@ -100,7 +102,7 @@ Slave from nameserver IP <INPUT TYPE="hidden" NAME="reczone" VALUE="@"> <INPUT TYPE="hidden" NAME="recaf" VALUE="IN"> <INPUT TYPE="hidden" NAME="rectype" VALUE="_mstr"> -<INPUT TYPE="text" NAME="recdata"> <INPUT TYPE="submit" VALUE="Slave domain"> +<INPUT TYPE="text" NAME="recdata"> <INPUT TYPE="submit" VALUE="Slave domain" onClick="return slave_areyousure()"> </FORM> <BR><BR><%= joblisting({'svcnum'=>$svcnum}, 1) %> </BODY></HTML> diff --git a/httemplate/view/svc_forward.cgi b/httemplate/view/svc_forward.cgi index c8d1d6213..97bb45615 100755 --- a/httemplate/view/svc_forward.cgi +++ b/httemplate/view/svc_forward.cgi @@ -25,10 +25,9 @@ my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ) print header('Mail Forward View', menubar( ( ( $pkgnum || $custnum ) - ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum", - "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + ? ( "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", ) - : ( "Cancel this (unaudited) account" => + : ( "Cancel this (unaudited) mail forward" => "${p}misc/cancel-unaudited.cgi?$svcnum" ) ), "Main menu" => $p, @@ -39,16 +38,25 @@ my($srcsvc,$dstsvc,$dst) = ( $svc_forward->dstsvc, $svc_forward->dst, ); +my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : ''; + my $svc = $part_svc->svc; -my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$srcsvc}) - or die "Corrupted database: no svc_acct.svcnum matching srcsvc $srcsvc"; -my $source = $svc_acct->email; + +my $source; +if ($srcsvc) { + my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$srcsvc}) + or die "Corrupted database: no svc_acct.svcnum matching srcsvc $srcsvc"; + $source = $svc_acct->email; +} else { + $source = $src; +} + my $destination; if ($dstsvc) { my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$dstsvc}) or die "Corrupted database: no svc_acct.svcnum matching dstsvc $dstsvc"; $destination = $svc_acct->email; -}else{ +} else { $destination = $dst; } diff --git a/httemplate/view/svc_www.cgi b/httemplate/view/svc_www.cgi index 442614491..6697b44bc 100644 --- a/httemplate/view/svc_www.cgi +++ b/httemplate/view/svc_www.cgi @@ -32,8 +32,7 @@ my $www = $domain_record->zone; print header('Website View', menubar( ( ( $custnum ) - ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum", - "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", + ? ( "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum", ) : ( "Cancel this (unaudited) website" => "${p}misc/cancel-unaudited.cgi?$svcnum" ) |