L<FS::cust_category> - Customer category class
-L<FS::cust_tag> - Customer tag class
-
-L<FS::part_tag> - Tag definition class
-
L<FS::cust_main_exemption> - Customer tax exemption class
L<FS::cust_main_note> - Customer note class
L<FS::queue_depend> - Job dependencies
-L<FS::msg_template> - Message templates (customer notices)
-
-L<FS::msgcat> - Message catalogs (error messages)
+L<FS::msgcat> - Message catalogs
L<FS::clientapi_session>
'View customer',
#'View Customer | View tickets',
'Edit customer',
- 'Edit customer tags',
'Edit referring customer',
'View customer history',
'Cancel customer',
{ rightname=>'Delete customer', desc=>"Enable customer deletions. Be very careful! Deleting a customer will remove all traces that this customer ever existed! It should probably only be used when auditing a legacy database. Normally, you cancel all of a customer's packages if they cancel service." }, #aka. deletecustomers
'Bill customer now', #NEW
'Bulk send customer notices', #NEW
- { rightname=>'View customers of all agents', global=>1 },
],
###
'Billing event reports',
'Receivables report',
'Financial reports',
-
- #{ rightname => 'List customers of all agents', global=>1 },
],
###
'Edit billing events',
{ rightname=>'Edit global billing events', global=>1 },
- 'Edit templates',
- { rightname=>'Edit global templates', global=>1 },
-
'Edit inventory',
{ rightname=>'Edit global inventory', global=>1 },
{ rightname=>'Broadband configuration' },
{ rightname=>'Broadband global configuration', global=>1 },
- #{ rightname=>'Edit employees', global=>1, },
- #{ rightname=>'Edit employee groupss', global=>1, },
-
{ rightname=>'Configuration', global=>1 }, #most of the rest of the configuraiton is not agent-virtualized
{ rightname=>'Configuration download', }, #description of how it affects
=cut
sub rooturl {
- my $url_string;
- if ( scalar(@_) ) {
- $url_string = shift;
- } else {
- # better to start with the client-provided URL
- my $cgi = &FS::UID::cgi;
- $url_string = $cgi->isa('Apache') ? $cgi->uri : $cgi->url;
- }
-
+ # better to start with the client-provided URL
+ my $cgi = &FS::UID::cgi;
+ my $url_string = $cgi->isa('Apache') ? $cgi->uri : $cgi->url;
$url_string =~ s/\?.*//;
#even though this is kludgy
validate($payinfo)
or return { 'error' => gettext('invalid_card') }; # . ": ". $self->payinfo
return { 'error' => gettext('unknown_card_type') }
- if $payinfo !~ /^99\d{14}$/ && cardtype($payinfo) eq "Unknown";
+ if cardtype($payinfo) eq "Unknown";
if ( length($p->{'paycvv'}) && $p->{'paycvv'} !~ /^\s*$/ ) {
if ( cardtype($payinfo) eq 'American Express card' ) {
stateid stateid_state );
$new->set( 'payby' => $p->{'auto'} ? 'CHEK' : 'DCHK' );
}
- $new->set( 'payinfo' => $cust_main->card_token || $payinfo );
+ $new->set( 'payinfo' => $payinfo );
$new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' );
my $error = $new->replace($cust_main);
if ( $error ) {
my $bill_error = $cust_main->bill
|| $cust_main->apply_payments_and_credits
- || $cust_main->realtime_collect;
+ || $cust_main->collect('realtime' => 1);
if ( $cust_main->balance > $old_balance
&& $cust_main->balance > 0
# " new customer: $bill_error"
# if $bill_error;
- $bill_error = $cust_main->realtime_collect(
- method => FS::payby->payby2bop( $packet->{payby} ),
- depend_jobnum => $placeholder->jobnum,
- );
+ if ($cust_main->_new_bop_required()) {
+ $bill_error = $cust_main->realtime_collect(
+ method => FS::payby->payby2bop( $packet->{payby} ),
+ depend_jobnum => $placeholder->jobnum,
+ );
+ } else {
+ $bill_error = $cust_main->collect('realtime' => 1);
+ }
#warn "[fs_signup_server] error collecting from new customer: $bill_error"
# if $bill_error;
+++ /dev/null
-package FS::ClientAPI_XMLRPC;
-
-=head1 NAME
-
-FS::ClientAPI_XMLRPC - Freeside XMLRPC accessible self-service API, on the backend
-
-=head1 SYNOPSIS
-
-This module implements the self-service API offered by xmlrpc.cgi and friends,
-but on a backend machine.
-
-=head1 DESCRIPTION
-
-Use this API to implement your own client "self-service" module vi XMLRPC.
-
-Each routine described in L<FS::SelfService> is available vi XMLRPC as the
-method FS.SelfService.XMLRPC.B<method>. All values are passed to the
-selfservice-server in a struct of strings. The return values are in a
-struct as strings, arrays, or structs as appropriate for the values
-described in L<FS::SelfService>.
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::SelfService::XMLRPC>, L<FS::SelfService>
-
-=cut
-
-use strict;
-
-use vars qw($DEBUG $AUTOLOAD);
-use FS::ClientAPI;
-
-$DEBUG = 0;
-$FS::ClientAPI::DEBUG = $DEBUG;
-
-sub AUTOLOAD {
- my $call = $AUTOLOAD;
- $call =~ s/^FS::(SelfService::|ClientAPI_)XMLRPC:://;
-
- warn "FS::ClientAPI_XMLRPC::AUTOLOAD $call\n" if $DEBUG;
-
- my $autoload = &ss2clientapi;
-
- if (exists($autoload->{$call})) {
- shift; #discard package name;
- #$call = "FS::SelfService::$call";
- #no strict 'refs';
- #&{$call}(@_);
- #FS::ClientAPI->dispatch($autoload->{$call}, @_);
- FS::ClientAPI->dispatch($autoload->{$call}, { @_ } );
- }else{
- die "No such procedure: $call";
- }
-}
-
-#terrible false laziness w/SelfService.pm
-# - fix at build time, by including some file in both selfserv and backend libs?
-# - or fix at runtime, by having selfservice client ask server for the list?
-sub ss2clientapi {
- {
- 'passwd' => 'passwd/passwd',
- 'chfn' => 'passwd/passwd',
- 'chsh' => 'passwd/passwd',
- 'login_info' => 'MyAccount/login_info',
- 'login' => 'MyAccount/login',
- 'logout' => 'MyAccount/logout',
- 'customer_info' => 'MyAccount/customer_info',
- 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
- 'invoice' => 'MyAccount/invoice',
- 'invoice_logo' => 'MyAccount/invoice_logo',
- 'list_invoices' => 'MyAccount/list_invoices', #?
- 'cancel' => 'MyAccount/cancel', #add to ss cgi!
- 'payment_info' => 'MyAccount/payment_info',
- 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
- 'process_payment' => 'MyAccount/process_payment',
- 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
- 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
- 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
- 'process_prepay' => 'MyAccount/process_prepay',
- 'realtime_collect' => 'MyAccount/realtime_collect',
- 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
- 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
- 'list_svc_usage' => 'MyAccount/list_svc_usage',
- 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
- 'list_support_usage' => 'MyAccount/list_support_usage',
- 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
- 'change_pkg' => 'MyAccount/change_pkg',
- 'order_recharge' => 'MyAccount/order_recharge',
- 'renew_info' => 'MyAccount/renew_info',
- 'order_renew' => 'MyAccount/order_renew',
- 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
- 'charge' => 'MyAccount/charge', #?
- 'part_svc_info' => 'MyAccount/part_svc_info',
- 'provision_acct' => 'MyAccount/provision_acct',
- 'provision_external' => 'MyAccount/provision_external',
- 'unprovision_svc' => 'MyAccount/unprovision_svc',
- 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
- 'create_ticket' => 'MyAccount/create_ticket',
- 'signup_info' => 'Signup/signup_info',
- 'skin_info' => 'MyAccount/skin_info',
- 'access_info' => 'MyAccount/access_info',
- 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
- 'new_customer' => 'Signup/new_customer',
- 'capture_payment' => 'Signup/capture_payment',
- 'agent_login' => 'Agent/agent_login',
- 'agent_logout' => 'Agent/agent_logout',
- 'agent_info' => 'Agent/agent_info',
- 'agent_list_customers' => 'Agent/agent_list_customers',
- 'mason_comp' => 'MasonComponent/mason_comp',
- 'call_time' => 'PrepaidPhone/call_time',
- 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
- 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
- 'bulk_processrow' => 'Bulk/processrow',
- 'check_username' => 'Bulk/check_username',
- #sg
- 'ping' => 'SGNG/ping',
- 'decompify_pkgs' => 'SGNG/decompify_pkgs',
- 'previous_payment_info' => 'SGNG/previous_payment_info',
- 'previous_payment_info_renew_info'
- => 'SGNG/previous_payment_info_renew_info',
- 'previous_process_payment' => 'SGNG/previous_process_payment',
- 'previous_process_payment_order_pkg'
- => 'SGNG/previous_process_payment_order_pkg',
- 'previous_process_payment_change_pkg'
- => 'SGNG/previous_process_payment_change_pkg',
- 'previous_process_payment_order_renew'
- => 'SGNG/previous_process_payment_order_renew',
- };
-}
-
-
-#XXX submit patch to SOAP::Lite
-
-use XMLRPC::Transport::HTTP;
-
-package XMLRPC::Transport::HTTP::Server;
-
-@XMLRPC::Transport::HTTP::Server::ISA = qw(SOAP::Transport::HTTP::Server);
-
-sub initialize; *initialize = \&XMLRPC::Server::initialize;
-sub make_fault; *make_fault = \&XMLRPC::Transport::HTTP::CGI::make_fault;
-sub make_response; *make_response = \&XMLRPC::Transport::HTTP::CGI::make_response;
-
-1;
"Solo",
);
-@base_items = qw(
-invoice_template
-invoice_latex
-invoice_latexreturnaddress
-invoice_latexfooter
-invoice_latexsmallfooter
-invoice_latexnotes
-invoice_latexcoupon
-invoice_html
-invoice_htmlreturnaddress
-invoice_htmlfooter
-invoice_htmlnotes
-logo.png
-logo.eps
-);
-
-my %msg_template_options = (
- 'type' => 'select-sub',
- 'options_sub' => sub { require FS::Record;
- require FS::agent;
- require FS::msg_template;
- map { $_->msgnum, $_->msgname }
- qsearch('msg_template', { disabled => '' });
- },
- 'option_sub' => sub { require FS::msg_template;
- my $msg_template = FS::msg_template->by_key(shift);
- $msg_template ? $msg_template->msgname : ''
- },
-);
-
+@base_items = qw (
+ invoice_template
+ invoice_latex
+ invoice_latexreturnaddress
+ invoice_latexfooter
+ invoice_latexsmallfooter
+ invoice_latexnotes
+ invoice_latexcoupon
+ invoice_html
+ invoice_htmlreturnaddress
+ invoice_htmlfooter
+ invoice_htmlnotes
+ logo.png
+ logo.eps
+ );
#Billing (81 items)
#Invoicing (50 items)
#...
#Unclassified (77 items)
+
@config_items = map { new FS::ConfItem $_ } (
{
{
'key' => 'alert_expiration',
- 'section' => 'notification',
+ 'section' => 'billing',
'description' => 'Enable alerts about billing method expiration (i.e. expiring credit cards).',
'type' => 'checkbox',
'per_agent' => 1,
{
'key' => 'alerter_template',
- 'section' => 'deprecated',
- 'description' => 'Template file for billing method expiration alerts (i.e. expiring credit cards).',
+ 'section' => 'billing',
+ 'description' => 'Template file for billing method expiration alerts (i.e. expiring credit cards). See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.7:Documentation:Administration#Credit_cards_and_Electronic_checks">billing documentation</a> for details.',
'type' => 'textarea',
'per_agent' => 1,
},
-
- {
- 'key' => 'alerter_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for credit card expiration alerts.',
- %msg_template_options,
- },
{
'key' => 'apacheip',
},
{
- 'key' => 'business-onlinepayment-test_transaction',
- 'section' => 'billing',
- 'description' => 'Turns on the Business::OnlinePayment test_transaction flag. Note that not all gateway modules support this flag; if yours does not, transactions will still be sent live.',
- 'type' => 'checkbox',
- },
-
- {
'key' => 'countrydefault',
'section' => 'UI',
'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
},
{
- 'key' => 'invoice_latextopmargin',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice topmargin setting. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
- 'key' => 'invoice_latexheadsep',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice headsep setting. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
- 'key' => 'invoice_latexaddresssep',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice separation between invoice header
-and customer address. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
- 'key' => 'invoice_latextextheight',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice textheight setting. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
'key' => 'invoice_latexnotes',
'section' => 'invoicing',
'description' => 'Notes section for LaTeX typeset PostScript invoices.',
},
{
- 'key' => 'invoice_latexextracouponspace',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice textheight space to reserve for a tear off coupon. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
- 'key' => 'invoice_latexcouponfootsep',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice separation between tear off coupon and footer. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
- 'key' => 'invoice_latexcouponamountenclosedsep',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice separation between total due and amount enclosed line. Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
- {
- 'key' => 'invoice_latexcoupontoaddresssep',
- 'section' => 'invoicing',
- 'description' => 'Optional LaTeX invoice separation between invoice data and the to address (usually invoice_latexreturnaddress). Include units.',
- 'type' => 'text',
- 'per_agent' => 1,
- 'validate' => sub { shift =~
- /^-?\d*\.?\d+(in|mm|cm|pt|em|ex|pc|bp|dd|cc|sp)$/
- ? '' : 'Invalid LaTex length';
- },
- },
-
- {
'key' => 'invoice_latexreturnaddress',
'section' => 'invoicing',
'description' => 'Return address for LaTeX typeset PostScript invoices.',
},
{
- 'key' => 'invoice_latexverticalreturnaddress',
- 'section' => 'invoicing',
- 'description' => 'Place the return address under the company logo rather than beside it.',
- 'type' => 'checkbox',
- 'per_agent' => 1,
- },
-
- {
- 'key' => 'invoice_latexcouponaddcompanytoaddress',
- 'section' => 'invoicing',
- 'description' => 'Add the company name to the To address on the remittance coupon because the return address does not contain it.',
- 'type' => 'checkbox',
- 'per_agent' => 1,
- },
-
- {
'key' => 'invoice_latexsmallfooter',
'section' => 'invoicing',
'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
'type' => 'textarea'
},
- {
- 'key' => 'invoice_print_pdf',
- 'section' => 'invoicing',
- 'description' => 'Store postal invoices for download in PDF format rather than printing them directly.',
- 'type' => 'checkbox',
- },
{
'key' => 'invoice_default_terms',
},
{
- 'key' => 'payment_receipt_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for payment receipts.',
- %msg_template_options,
- },
-
- {
'key' => 'payment_receipt_email',
- 'section' => 'deprecated',
- 'description' => 'Template file for payment receipts. Payment receipts are sent to the customer email invoice destination(s) when a payment is received.',
+ 'section' => 'billing',
+ 'description' => 'Template file for payment receipts. Payment receipts are sent to the customer email invoice destination(s) when a payment is received. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available: <ul><li><code>$date</code> <li><code>$name</code> <li><code>$paynum</code> - Freeside payment number <li><code>$paid</code> - Amount of payment <li><code>$payby</code> - Payment type (Card, Check, Electronic check, etc.) <li><code>$payinfo</code> - Masked credit card number or check number <li><code>$balance</code> - New balance<li><code>$pkg</code> - Package (requires payment_receipt-trigger set to "when payment is applied".)</ul>',
'type' => [qw( checkbox textarea )],
},
{
'key' => 'payment_receipt-trigger',
- 'section' => 'notification',
+ 'section' => 'billing',
'description' => 'When payment receipts are triggered. Defaults to when payment is made.',
'type' => 'select',
'select_hash' => [
'description' => 'Run billing for signup server signups immediately, and do not provision accounts which subsequently have a balance.',
'type' => 'checkbox',
},
-
{
'key' => 'signup_server-classnum2',
'section' => 'self-service',
},
{
- 'key' => 'selfservice-xmlrpc',
- 'section' => 'self-service',
- 'description' => 'Run a standalone self-service XML-RPC server on the backend (on port 8080).',
- 'type' => 'checkbox',
- },
-
- {
'key' => 'backend-realtime',
'section' => 'billing',
'description' => 'Run billing for backend signups immediately.',
},
{
- 'key' => 'decline_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for credit card and electronic check decline messages.',
- %msg_template_options,
- },
-
- {
'key' => 'declinetemplate',
- 'section' => 'deprecated',
+ 'section' => 'billing',
'description' => 'Template file for credit card and electronic check decline emails.',
'type' => 'textarea',
},
{
'key' => 'emaildecline',
- 'section' => 'notification',
+ 'section' => 'billing',
'description' => 'Enable emailing of credit card and electronic check decline notices.',
'type' => 'checkbox',
},
{
'key' => 'emaildecline-exclude',
- 'section' => 'notification',
+ 'section' => 'billing',
'description' => 'List of error messages that should not trigger email decline notices, one per line.',
'type' => 'textarea',
},
{
- 'key' => 'cancel_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for cancellation emails.',
- %msg_template_options,
- },
-
- {
'key' => 'cancelmessage',
- 'section' => 'deprecated',
+ 'section' => 'billing',
'description' => 'Template file for cancellation emails.',
'type' => 'textarea',
},
{
'key' => 'cancelsubject',
- 'section' => 'deprecated',
+ 'section' => 'billing',
'description' => 'Subject line for cancellation emails.',
'type' => 'text',
},
{
'key' => 'emailcancel',
- 'section' => 'notification',
- 'description' => 'Enable emailing of cancellation notices. Make sure to select the template in the cancel_msgnum option.',
+ 'section' => 'billing',
+ 'description' => 'Enable emailing of cancellation notices. Make sure to fill in the cancelmessage and cancelsubject configuration values as well.',
'type' => 'checkbox',
},
},
{
- 'key' => 'welcome_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for welcome messages when a svc_acct record is created.',
- %msg_template_options,
- },
-
- {
'key' => 'welcome_email',
- 'section' => 'deprecated',
- 'description' => 'Template file for welcome email. Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created.',
+ 'section' => '',
+ 'description' => 'Template file for welcome email. Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code></ul>',
'type' => 'textarea',
'per_agent' => 1,
},
{
'key' => 'welcome_email-from',
- 'section' => 'deprecated',
+ 'section' => '',
'description' => 'From: address header for welcome email',
'type' => 'text',
'per_agent' => 1,
{
'key' => 'welcome_email-subject',
- 'section' => 'deprecated',
+ 'section' => '',
'description' => 'Subject: header for welcome email',
'type' => 'text',
'per_agent' => 1,
{
'key' => 'welcome_email-mimetype',
- 'section' => 'deprecated',
+ 'section' => '',
'description' => 'MIME type for welcome email',
'type' => 'select',
'select_enum' => [ 'text/plain', 'text/html' ],
'type' => 'textarea',
},
-# {
-# 'key' => 'warning_msgnum',
-# 'section' => 'notification',
-# 'description' => 'Template to use for warning messages, sent to the customer email invoice destination(s) when a svc_acct record has its usage drop below a threshold.',
-# %msg_template_options,
-# },
-
{
'key' => 'warning_email',
- 'section' => 'notification',
+ 'section' => '',
'description' => 'Template file for warning email. Warning emails are sent to the customer email invoice destination(s) each time a svc_acct record has its usage drop below a threshold or 0. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code> <li><code>$column</code> <li><code>$amount</code> <li><code>$threshold</code></ul>',
'type' => 'textarea',
},
{
'key' => 'warning_email-from',
- 'section' => 'notification',
+ 'section' => '',
'description' => 'From: address header for warning email',
'type' => 'text',
},
{
'key' => 'warning_email-cc',
- 'section' => 'notification',
+ 'section' => '',
'description' => 'Additional recipient(s) (comma separated) for warning email when remaining usage reaches zero.',
'type' => 'text',
},
{
'key' => 'warning_email-subject',
- 'section' => 'notification',
+ 'section' => '',
'description' => 'Subject: header for warning email',
'type' => 'text',
},
{
'key' => 'warning_email-mimetype',
- 'section' => 'notification',
+ 'section' => '',
'description' => 'MIME type for warning email',
'type' => 'select',
'select_enum' => [ 'text/plain', 'text/html' ],
},
{
- 'key' => 'global_unique-pbx_title',
- 'section' => '',
- 'description' => 'Global phone number uniqueness control: enabled (usual setting - svc_pbx.title must be unique), or disabled turns off duplicate checking for this field.',
- 'type' => 'select',
- 'select_enum' => [ 'enabled', 'disabled' ],
- },
-
- {
'key' => 'svc_external-skip_manual',
'section' => 'UI',
'description' => 'When provisioning svc_external services, skip manual entry of id and title fields in the UI. Usually used in conjunction with an export that populates these fields (i.e. artera_turbo).',
}
},
},
- {
- 'key' => 'ticket_system-force_default_queueid',
- 'section' => '',
- 'description' => 'Disallow queue selection when creating new tickets from customer view.',
- 'type' => 'checkbox',
- },
+
{
'key' => 'ticket_system-selfservice_queueid',
'section' => '',
},
{
- 'key' => 'cgp_rule-domain_templates',
- 'section' => '',
- 'description' => 'Communigate Pro rule templates for domains, one per line, "svcnum Name"',
- 'type' => 'textarea',
- },
-
- {
'key' => 'svc_forward-no_srcsvc',
'section' => '',
'description' => "Don't allow forwards from existing accounts, only arbitrary addresses. Useful when exporting to systems such as Communigate Pro which treat forwards in this fashion.",
},
{
- 'key' => 'impending_recur_msgnum',
- 'section' => 'notification',
- 'description' => 'Template to use for alerts about first-time recurring billing.',
- %msg_template_options,
- },
-
- {
'key' => 'impending_recur_template',
- 'section' => 'deprecated',
+ 'section' => 'billing',
'description' => 'Template file for alerts about looming first time recurrant billing. See the <a href="http://search.cpan.org/dist/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitition language. Also see packages with a <a href="../browse/part_pkg.cgi">flat price plan</a> The following variables are available<ul><li><code>$packages</code> allowing <code>$packages->[0]</code> thru <code>$packages->[n]</code> <li><code>$package</code> the first package, same as <code>$packages->[0]</code> <li><code>$recurdates</code> allowing <code>$recurdates->[0]</code> thru <code>$recurdates->[n]</code> <li><code>$recurdate</code> the first recurdate, same as <code>$recurdate->[0]</code> <li><code>$first</code> <li><code>$last</code></ul>',
# <li><code>$payby</code> <li><code>$expdate</code> most likely only confuse
'type' => 'textarea',
},
{
- 'key' => 'disable_settings_changes',
- 'section' => '',
- 'description' => 'Disable all settings changes, for demos, except for the usernames given in the comma-separated list.',
- 'type' => [qw( checkbox text )],
- },
-
- {
'key' => 'cust_main-edit_agent_custid',
'section' => 'UI',
'description' => 'Enable editing of the agent_custid field.',
},
{
- 'key' => 'cdr-charged_party-field',
- 'section' => '',
- 'description' => 'Set the charged_party field of CDRs to this field.',
- 'type' => 'select-sub',
- 'options_sub' => sub { my $fields = FS::cdr->table_info->{'fields'};
- map { $_ => $fields->{$_}||$_ }
- grep { $_ !~ /^(acctid|charged_party)$/ }
- FS::Schema::dbdef->table('cdr')->columns;
- },
- 'option_sub' => sub { my $f = shift;
- FS::cdr->table_info->{'fields'}{$f} || $f;
- },
- },
-
- #probably deprecate in favor of cdr-charged_party-field above
- {
'key' => 'cdr-charged_party-accountcode',
'section' => '',
'description' => 'Set the charged_party field of CDRs to the accountcode.',
},
{
- 'key' => 'mc-outbound_packages',
- 'section' => '',
- 'description' => "Don't use this.",
- 'type' => 'select-part_pkg',
- 'multiple' => 1,
- },
-
- {
'key' => 'disable-cust-pkg_class',
'section' => 'UI',
'description' => 'Disable the two-step dropdown for selecting package class and package, and return to the classic single dropdown.',
'type' => 'checkbox',
},
- {
- 'key' => 'cust_main-exports',
- 'section' => '',
- 'description' => 'Export(s) to call on cust_main insert, modification and deletion.',
- 'type' => 'select-sub',
- 'multiple' => 1,
- 'options_sub' => sub {
- require FS::Record;
- require FS::part_export;
- my @part_export =
- map { qsearch( 'part_export', {exporttype => $_ } ) }
- keys %{FS::part_export::export_info('cust_main')};
- map { $_->exportnum => $_->exporttype.' to '.$_->machine } @part_export;
- },
- 'option_sub' => sub {
- require FS::Record;
- require FS::part_export;
- my $part_export = FS::Record::qsearchs(
- 'part_export', { 'exportnum' => shift }
- );
- $part_export
- ? $part_export->exporttype.' to '.$part_export->machine
- : '';
- },
- },
-
- {
- 'key' => 'cust_tag-location',
- 'section' => 'UI',
- 'description' => 'Location where customer tags are displayed.',
- 'type' => 'select',
- 'select_enum' => [ 'misc_info', 'top' ],
- },
-
- {
- 'key' => 'maestro-status_test',
- 'section' => 'UI',
- 'description' => 'Display a link to the maestro status test page on the customer view page',
- 'type' => 'checkbox',
- },
-
-
{ key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
{ key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
{ key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
use vars qw( @ISA @EXPORT_OK);
use Exporter;
-use FS::Record qw(qsearch qsearchs);
+use FS::Record qw(qsearch);
use FS::Conf;
use FS::cust_main;
use FS::Misc;
}
return if(!@customers);
foreach my $customer (@customers) {
- next if !($customer->ncancelled_pkgs); # skip inactive customers
my $paydate = $customer->paydate;
next if $paydate =~ /^\s*$/; # skip empty expiration dates
if (grep { $expire_time < $_date + $_ &&
$expire_time > $_date + $_ - $window_time }
($warning_time, $urgent_time, $panic_time) ) {
- # Send an expiration notice.
my $agentnum = $customer->agentnum;
- my $error = '';
-
- my $msgnum = $conf->config('alerter_msgnum', $agentnum);
- if ( $msgnum ) { # new hotness
- my $msg_template = qsearchs('msg_template', { msgnum => $msgnum } );
- $error = $msg_template->send('cust_main' => $customer);
- }
- else { #!$msgnum, the hard way
- $mail_sender = $conf->config('invoice_from', $agentnum);
- $failure_recipient = $conf->config('invoice_from', $agentnum)
- || 'postmaster';
-
- my @alerter_template = $conf->config('alerter_template', $agentnum)
- or die 'cannot load config file alerter_template';
-
- my $alerter = new Text::Template(TYPE => 'ARRAY',
- SOURCE => [
- map "$_\n", @alerter_template
- ])
- or die "can't create Text::Template object: $Text::Template::ERROR";
-
- $alerter->compile()
- or die "can't compile template: $Text::Template::ERROR";
-
+ $mail_sender = $conf->config('invoice_from', $agentnum);
+ $failure_recipient = $conf->config('invoice_from', $agentnum)
+ || 'postmaster';
+
+ my @alerter_template = $conf->config('alerter_template', $agentnum)
+ or die 'cannot load config file alerter_template';
+
+ my $alerter = new Text::Template(TYPE => 'ARRAY',
+ SOURCE => [
+ map "$_\n", @alerter_template
+ ])
+ or die "can't create Text::Template object: $Text::Template::ERROR";
+
+ $alerter->compile()
+ or die "can't compile template: $Text::Template::ERROR";
+
+ my @packages = $customer->ncancelled_pkgs;
+ if(@packages) {
my @invoicing_list = $customer->invoicing_list;
my @to_addrs = grep { $_ ne 'POST' } @invoicing_list;
if(@to_addrs) {
$fill_in{'payby'} = 'current method';
}
# Send it already!
- $error = FS::Misc::send_email (
+ my $error = FS::Misc::send_email (
from => $mail_sender,
to => [ @to_addrs ],
subject => 'Billing Arrangement Expiration',
body => [ $alerter->fill_in( HASH => \%fill_in ) ],
);
- }
- else { # if(@to_addrs)
- push @{$agent_failure_body{$customer->agentnum}},
- sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
- $custnum,
- $first . " " . $last . " " . $company,
- $payby,
- $paydate,
- $daytime,
- $night );
- }
- } # if($msgnum)
-
-# should we die here rather than report failure as below?
- die "can't send expiration alert: $error"
- if $error;
-
+ die "can't send expiration alert: $error"
+ if $error;
+ }
+ else { # if(@to_addrs)
+ push @{$agent_failure_body{$customer->agentnum}},
+ sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
+ $custnum,
+ $first . " " . $last . " " . $company,
+ $payby,
+ $paydate,
+ $daytime,
+ $night );
+ }
+ } # if(@packages)
} # if(expired)
} # foreach(@customers)
#we're at now now (and later).
my($time) = $^T;
- my $conf = new FS::Conf;
- my $error = '';
my $integer = driver_name =~ /^mysql/ ? 'SIGNED' : 'INTEGER';
push @cust_pkgs, $cust_pkg[0];
shift @cust_pkg;
}
- my $msgnum = $conf->config('impending_recur_msgnum',$cust_main->agentnum);
- if ( $msgnum ) {
- my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
- $error = $msg_template->send('cust_main' => $cust_main);
- }
- else {
- $error = $cust_main->notify( 'impending_recur_template',
+ my $error =
+ $cust_main->notify( 'impending_recur_template',
'extra_fields' => { 'packages' => \@packages,
'recurdates' => \@recurdates,
'package' => $packages[0],
'recurdate' => $recurdates[0],
},
);
- } #if $msgnum
warn "Error notifying, custnum ". $cust_main->custnum. ": $error" if $error;
unless ($error) {
package FS::Daemon;
use vars qw( @ISA @EXPORT_OK );
-use vars qw( $pid_dir $me $pid_file $sigint $sigterm $NOSIG $logfile );
+use vars qw( $pid_dir $me $pid_file $sigint $sigterm $logfile );
use Exporter;
use Fcntl qw(:flock);
use POSIX qw(setsid);
use IO::File;
-use File::Basename;
-use File::Slurp qw(slurp);
use Date::Format;
#this is a simple refactoring of the stuff from freeside-queued, just to
$pid_dir = '/var/run';
-$NOSIG = 0;
-$PID_NEWSTYLE = 0;
-
sub daemonize1 {
$me = shift;
- $pid_file = $pid_dir;
- if ( $PID_NEWSTYLE ) {
- $pid_file .= '/freeside';
- mkdir $pid_file unless -d $pid_file;
- chown $FS::UID::freeside_uid, -1, $pid_file;
- }
- $pid_file .= "/$me";
+ $pid_file = "$pid_dir/$me";
$pid_file .= '.'.shift if scalar(@_);
$pid_file .= '.pid';
print "$me started with pid $pid\n"; #logging to $log_file\n";
exit unless $pid_file;
my $pidfh = new IO::File ">$pid_file" or exit;
- chown $FS::UID::freeside_uid, -1, $pid_file;
print $pidfh "$pid\n";
exit;
}
#$SIG{CHLD} = \&REAPER;
$sigterm = 0;
$sigint = 0;
- unless ( $NOSIG ) {
- $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
- $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
- }
+ $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
+ $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
}
sub drop_root {
sub logfile { $logfile = shift; } #_logmsg('test'); }
sub myexit {
- chomp( my $pid = slurp($pid_file) );
- unlink $pid_file if -e $pid_file && $$ == $pid;
+ unlink $pid_file if -e $pid_file;
exit;
}
sub _die {
- die @_ if $^S; # $^S = 1 during an eval(), don't break exception handling
my $msg = shift;
-
- chomp( my $pid = slurp($pid_file) );
- unlink $pid_file if -e $pid_file && $$ == $pid;
-
+ unlink $pid_file if -e $pid_file;
_logmsg($msg);
}
close $log;
}
-1;
+++ /dev/null
-package FS::Maestro;
-
-use Date::Format;
-use FS::Conf;
-use FS::Record qw( qsearchs );
-use FS::cust_main;
-
-sub customer_status {
- my( $custnum ) = shift; #@_;
- my $svcnum = @_ ? shift : '';
-
- my $curuser = $FS::CurrentUser::CurrentUser;
-
- my $cust_main = qsearchs({
- 'table' => 'cust_main',
- 'hashref' => { 'custnum' => $custnum },
- 'extra_sql' => ' AND '. $curuser->agentnums_sql,
- })
- or return { 'status' => 'E',
- 'error' => "custnum $custnum not found" };
-
- my( $svc_pbx, $good_till, $outbound_service ) = ( '', '', '' );
- my %result = ();
- if ( $svcnum ) {
-
- ###
- # reseller scenario to maestro (customer w/ multiple packages)
- ###
-
- # find $svc_pbx
-
- $svc_pbx = qsearchs({
- 'table' => 'svc_pbx',
- 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
- ' LEFT JOIN cust_pkg USING ( pkgnum ) ',
- 'hashref' => { 'svcnum' => $svcnum },
- 'extra_sql' => " AND custnum = $custnum",
- })
- or return { 'status' => 'E',
- 'error' => "svcnum $svcnum not found" };
-
- #status in the reseller scenario
-
- my $cust_pkg = $svc_pbx->cust_svc->cust_pkg;
-
- $result{'status'} = substr($cust_pkg->ucfirst_status,0,1);
-
- # find "outbound service" y/n
-
- #XXX outbound service per-reseller ?
- #my @cust_pkg = $cust_main->cust_pkg;
- #
- #my $conf = new FS::Conf;
- #my %outbound_pkgs = map { $_=>1 } $conf->config('mc-outbound_packages');
- #my $outbound_service =
- # scalar( grep { $outbound_pkgs{ $_->pkgpart }
- # && !$_->get('cancel')
- # }
- # @cust_pkg
- # )
- # ? 1 : 0;
-
- # find "good till" date/time stamp (this package)
-
- $good_till = time2str('%c', $cust_pkg->bill || time );
-
- } else {
-
- ###
- # regular customer to maestro (single package)
- ###
-
- my @cust_pkg = $cust_main->cust_pkg;
-
- #things specific to the non-reseller scenario
-
- $result{'status'} = substr($cust_main->ucfirst_status,0,1);
-
- $result{'products'} =
- [ map $_->pkgpart, grep !$_->get('cancel'), @cust_pkg ];
-
- #find svc_pbx
-
- my @cust_svc = map $_->cust_svc, @cust_pkg;
-
- my @cust_svc_pbx =
- grep { my($n,$l,$t) = $_->label; $t eq 'svc_pbx' }
- @cust_svc;
-
- if ( ! @cust_svc_pbx ) {
- return { 'status' => 'E',
- 'error' => "customer $custnum has no conference service" };
- } elsif ( scalar(@cust_svc_pbx) > 1 ) {
- return { 'status' => 'E',
- 'error' =>
- "customer $custnum has more than one conference".
- " service (reseller?); specify a svcnum as a second argument",
- };
- }
-
- my $cust_svc_pbx = $cust_svc_pbx[0];
-
- $svc_pbx = $cust_svc_pbx->svc_x;
-
- # find "outbound service" y/n
-
- my $conf = new FS::Conf;
- my %outbound_pkgs = map { $_=>1 } $conf->config('mc-outbound_packages');
- $outbound_service =
- scalar( grep { $outbound_pkgs{ $_->pkgpart }
- && !$_->get('cancel')
- }
- @cust_pkg
- )
- ? 1 : 0;
-
- # find "good till" date/time stamp
-
- my @active_cust_pkg =
- sort { $a->bill <=> $b->bill }
- grep { !$_->get('cancel') && $_->part_pkg->freq ne '0' }
- @cust_pkg;
- $good_till = time2str('%c', $active_cust_pkg[0]->bill || time );
-
- }
-
- return {
- 'name' => $cust_main->name,
- 'email' => $cust_main->invoicing_list_emailonly_scalar,
- 'max_lines' => $svc_pbx ? $svc_pbx->max_extensions : '',
- 'max_simultaneous' => $svc_pbx ? $svc_pbx->max_simultaneous : '',
- 'outbound_service' => $outbound_service,
- 'good_till' => $good_till,
- %result,
- };
-
-}
-
-1;
use HTML::FormatText;
use HTML::Defang;
use JSON;
-# use XMLRPC::Transport::HTTP;
-# use XMLRPC::Lite; # for XMLRPC::Serializer
use MIME::Base64;
use IO::Handle;
use IO::File;
use FS::part_pkg_report_option;
use FS::cust_attachment;
use FS::h_cust_pkg;
- use FS::h_inventory_item;
use FS::h_svc_acct;
use FS::h_svc_broadband;
use FS::h_svc_domain;
use FS::cgp_rule;
use FS::cgp_rule_condition;
use FS::cgp_rule_action;
- use FS::bill_batch;
- use FS::cust_bill_batch;
- use FS::rate_time;
- use FS::rate_time_interval;
- use FS::msg_template;
- use FS::part_tag;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
use vars qw($Nobody $SystemUser);
use RT;
- use RT::Util;
use RT::Tickets;
use RT::Transactions;
use RT::Users;
use RT::Interface::Web::Request;
- #nother undeclared web UI dep (for ticket links graph)
- use IPC::Run::SafeHandles;
-
#slow, unreliable, segfaults and is optional
#see rt/html/Ticket/Elements/ShowTransactionAttachments
#use Text::Quoted;
$m->comp('/elements/errorpage.html', @_);
}
- sub errorpage_popup {
- use vars qw($m);
- $m->comp('/elements/errorpage-popup.html', @_);
- }
-
sub redirect {
my( $location ) = @_;
use vars qw($m);
my $html_defang = new HTML::Defang (%defang_opts);
- my $js_string_sub = sub {
- #${$_[0]} =~ s/(['\\\n])/'\\'.($1 eq "\n" ? 'n' : $1)/ge;
- ${$_[0]} =~ s/(['\\])/\\$1/g;
- ${$_[0]} =~ s/\r/\\r/g;
- ${$_[0]} =~ s/\n/\\n/g;
- ${$_[0]} = "'". ${$_[0]}. "'";
- };
-
my $fs_interp = new HTML::Mason::Interp (
%interp,
- escape_flags => { 'js_string' => $js_string_sub,
+ escape_flags => { 'js_string' => sub {
+ #${$_[0]} =~ s/(['\\\n])/'\\'.($1 eq "\n" ? 'n' : $1)/ge;
+ ${$_[0]} =~ s/(['\\])/\\$1/g;
+ ${$_[0]} =~ s/\n/\\n/g;
+ ${$_[0]} = "'". ${$_[0]}. "'";
+ },
'defang' => sub {
${$_[0]} = $html_defang->defang(${$_[0]});
},
my $rt_interp = new HTML::Mason::Interp (
%interp,
- escape_flags => { 'h' => \&RT::Interface::Web::EscapeUTF8,
- 'js_string' => $js_string_sub,
- },
+ escape_flags => { 'h' => \&RT::Interface::Web::EscapeUTF8 },
compiler => HTML::Mason::Compiler::ToObject->new(
default_escape_flags => 'h',
allow_globals => [qw(%session)],
use IPC::Run qw( run timeout ); # for _pslatex
use IPC::Run3; # for do_print... should just use IPC::Run i guess
use File::Temp;
-use Tie::IxHash;
#do NOT depend on any FS:: modules here, causes weird (sometimes unreproducable
#until on client machine) dependancy loops. put them in FS::Misc::Something
#instead
@ISA = qw( Exporter );
-@EXPORT_OK = qw( send_email generate_email send_fax
+@EXPORT_OK = qw( generate_email send_email send_fax
states_hash counties cities state_label
card_types
- pkg_freqs
generate_ps generate_pdf do_print
csv_from_fixed
);
Miscellaneous subroutines. This module contains miscellaneous subroutines
called from multiple other modules. These are not OO or necessarily related,
-but are collected here to eliminate code duplication.
+but are collected here to elimiate code duplication.
=head1 SUBROUTINES
=over 4
+=item generate_email OPTION => VALUE ...
+
+Options:
+
+=over 4
+
+=item from
+
+Sender address, required
+
+=item to
+
+Recipient address, required
+
+=item subject
+
+email subject, required
+
+=item html_body
+
+Email body (HTML alternative). Arrayref of lines, or scalar.
+
+Will be placed inside an HTML <BODY> tag.
+
+=item text_body
+
+Email body (Text alternative). Arrayref of lines, or scalar.
+
+=back
+
+Returns an argument list to be passsed to L<send_email>.
+
+=cut
+
+#false laziness w/FS::cust_bill::generate_email
+
+use MIME::Entity;
+use HTML::Entities;
+
+sub generate_email {
+ my %args = @_;
+
+ my $me = '[FS::Misc::generate_email]';
+
+ my %return = (
+ 'from' => $args{'from'},
+ 'to' => $args{'to'},
+ 'subject' => $args{'subject'},
+ );
+
+ #if (ref($args{'to'}) eq 'ARRAY') {
+ # $return{'to'} = $args{'to'};
+ #} else {
+ # $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ }
+ # $self->cust_main->invoicing_list
+ # ];
+ #}
+
+ warn "$me creating HTML/text multipart message"
+ if $DEBUG;
+
+ $return{'nobody'} = 1;
+
+ my $alternative = build MIME::Entity
+ 'Type' => 'multipart/alternative',
+ 'Encoding' => '7bit',
+ 'Disposition' => 'inline'
+ ;
+
+ my $data;
+ if ( ref($args{'text_body'}) eq 'ARRAY' ) {
+ $data = $args{'text_body'};
+ } else {
+ $data = [ split(/\n/, $args{'text_body'}) ];
+ }
+
+ $alternative->attach(
+ 'Type' => 'text/plain',
+ #'Encoding' => 'quoted-printable',
+ 'Encoding' => '7bit',
+ 'Data' => $data,
+ 'Disposition' => 'inline',
+ );
+
+ my @html_data;
+ if ( ref($args{'html_body'}) eq 'ARRAY' ) {
+ @html_data = @{ $args{'html_body'} };
+ } else {
+ @html_data = split(/\n/, $args{'html_body'});
+ }
+
+ $alternative->attach(
+ 'Type' => 'text/html',
+ 'Encoding' => 'quoted-printable',
+ 'Data' => [ '<html>',
+ ' <head>',
+ ' <title>',
+ ' '. encode_entities($return{'subject'}),
+ ' </title>',
+ ' </head>',
+ ' <body bgcolor="#e8e8e8">',
+ @html_data,
+ ' </body>',
+ '</html>',
+ ],
+ 'Disposition' => 'inline',
+ #'Filename' => 'invoice.pdf',
+ );
+
+ #no other attachment:
+ # multipart/related
+ # multipart/alternative
+ # text/plain
+ # text/html
+
+ $return{'content-type'} = 'multipart/related';
+ $return{'mimeparts'} = [ $alternative ];
+ $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
+ #$return{'disposition'} = 'inline';
+
+ %return;
+
+}
+
=item send_email OPTION => VALUE ...
Options:
$smtp_opt{'ssl'} = 1 if defined($enc) && $enc eq 'tls';
$transport = Email::Sender::Transport::SMTP->new( %smtp_opt );
}
-
- local $@; # just in case
- eval { sendmail($message, { transport => $transport }) };
-
- if(ref($@) and $@->isa('Email::Sender::Failure')) {
- return ($@->code ? $@->code.' ' : '').$@->message
- }
- else {
- return $@;
- }
-}
-
-=item generate_email OPTION => VALUE ...
-
-Options:
-
-=over 4
-
-=item from
-
-Sender address, required
-
-=item to
-
-Recipient address, required
-
-=item subject
-
-email subject, required
-
-=item html_body
-
-Email body (HTML alternative). Arrayref of lines, or scalar.
-
-Will be placed inside an HTML <BODY> tag.
-
-=item text_body
-
-Email body (Text alternative). Arrayref of lines, or scalar.
-
-=back
-
-Constructs a multipart message from text_body and html_body.
-
-=cut
-
-#false laziness w/FS::cust_bill::generate_email
-
-use MIME::Entity;
-use HTML::Entities;
-
-sub generate_email {
- my %args = @_;
-
- my $me = '[FS::Misc::generate_email]';
-
- my %return = (
- 'from' => $args{'from'},
- 'to' => $args{'to'},
- 'subject' => $args{'subject'},
- );
-
- #if (ref($args{'to'}) eq 'ARRAY') {
- # $return{'to'} = $args{'to'};
- #} else {
- # $return{'to'} = [ grep { $_ !~ /^(POST|FAX)$/ }
- # $self->cust_main->invoicing_list
- # ];
- #}
-
- warn "$me creating HTML/text multipart message"
- if $DEBUG;
-
- $return{'nobody'} = 1;
-
- my $alternative = build MIME::Entity
- 'Type' => 'multipart/alternative',
- 'Encoding' => '7bit',
- 'Disposition' => 'inline'
- ;
-
- my $data;
- if ( ref($args{'text_body'}) eq 'ARRAY' ) {
- $data = $args{'text_body'};
- } else {
- $data = [ split(/\n/, $args{'text_body'}) ];
- }
- $alternative->attach(
- 'Type' => 'text/plain',
- #'Encoding' => 'quoted-printable',
- 'Encoding' => '7bit',
- 'Data' => $data,
- 'Disposition' => 'inline',
- );
+ eval { sendmail($message, { transport => $transport }); };
+ ref($@) eq 'Email::Sender::Failure'
+ ? ( $@->code ? $@->code.' ' : '' ). $@->message
+ : $@;
- my @html_data;
- if ( ref($args{'html_body'}) eq 'ARRAY' ) {
- @html_data = @{ $args{'html_body'} };
- } else {
- @html_data = split(/\n/, $args{'html_body'});
- }
-
- $alternative->attach(
- 'Type' => 'text/html',
- 'Encoding' => 'quoted-printable',
- 'Data' => [ '<html>',
- ' <head>',
- ' <title>',
- ' '. encode_entities($return{'subject'}),
- ' </title>',
- ' </head>',
- ' <body bgcolor="#e8e8e8">',
- @html_data,
- ' </body>',
- '</html>',
- ],
- 'Disposition' => 'inline',
- #'Filename' => 'invoice.pdf',
- );
-
- #no other attachment:
- # multipart/related
- # multipart/alternative
- # text/plain
- # text/html
-
- $return{'content-type'} = 'multipart/related';
- $return{'mimeparts'} = [ $alternative ];
- $return{'type'} = 'multipart/alternative'; #Content-Type of first part...
- #$return{'disposition'} = 'inline';
-
- %return;
-
-}
-
-=item process_send_email OPTION => VALUE ...
-
-Takes arguments as per generate_email() and sends the message. This
-will die on any error and can be used in the job queue.
-
-=cut
-
-sub process_send_email {
- my %message = @_;
- my $error = send_email(generate_email(%message));
- die "$error\n" if $error;
- '';
}
=item send_fax OPTION => VALUE ...
\%card_types;
}
-=item pkg_freqs
-
-Returns a hash reference of allowed package billing frequencies.
-
-=cut
-
-sub pkg_freqs {
- tie my %freq, 'Tie::IxHash', (
- '0' => '(no recurring fee)',
- '1h' => 'hourly',
- '1d' => 'daily',
- '2d' => 'every two days',
- '3d' => 'every three days',
- '1w' => 'weekly',
- '2w' => 'biweekly (every 2 weeks)',
- '1' => 'monthly',
- '45d' => 'every 45 days',
- '2' => 'bimonthly (every 2 months)',
- '3' => 'quarterly (every 3 months)',
- '4' => 'every 4 months',
- '137d' => 'every 4 1/2 months (137 days)',
- '6' => 'semiannually (every 6 months)',
- '12' => 'annually',
- '13' => 'every 13 months (annually +1 month)',
- '24' => 'biannually (every 2 years)',
- '36' => 'triannually (every 3 years)',
- '48' => '(every 4 years)',
- '60' => '(every 5 years)',
- '120' => '(every 10 years)',
- ) ;
- \%freq;
-}
-
=item generate_ps FILENAME
Returns an postscript rendition of the LaTex file, as a scalar.
$self->set(@_);
}
-=item exists COLUMN
-
-Returns true if the column/field/key COLUMN exists.
-
-=cut
-
-sub exists {
- my($self,$field) = @_;
- exists($self->{Hash}->{$field});
-}
-
=item AUTLOADED METHODS
$record->column is a synonym for $record->get('column');
format_headers => $opt->{format_headers},
format_sep_chars => $opt->{format_sep_chars},
format_fixedlength_formats => $opt->{format_fixedlength_formats},
- format_row_callbacks => $opt->{format_row_callbacks},
#per-import
job => $job,
file => $file,
=item format_fixedlength_formats
-=item format_row_callbacks
-
=item params
=item job
my $param = shift;
warn "$me batch_import call with params: \n". Dumper($param)
- ;# if $DEBUG;
+ if $DEBUG;
my $table = $param->{table};
my $formats = $param->{formats};
? $param->{'format_fixedlength_formats'}{ $param->{'format'} }
: '';
- my $row_callback =
- $param->{'format_row_callbacks'}
- ? $param->{'format_row_callbacks'}{ $param->{'format'} }
- : '';
-
my @fields = @{ $formats->{ $format } };
my $row = 0;
next if $line =~ /^\s*$/; #skip empty lines
- $line = &{$row_callback}($line) if $row_callback;
-
$parser->parse($line) or do {
$dbh->rollback if $oldAutoCommit;
return "can't parse: ". $parser->error_input();
'unique' => [],
'index' => [ ['disabled'] ],
},
-
- 'cust_tag' => {
- 'columns' => [
- 'custtagnum', 'serial', '', '', '', '',
- 'custnum', 'int', '', '', '', '',
- 'tagnum', 'int', '', '', '', '',
- ],
- 'primary_key' => 'custtagnum',
- 'unique' => [ [ 'custnum', 'tagnum' ] ],
- 'index' => [ [ 'custnum' ] ],
- },
-
- 'part_tag' => {
- 'columns' => [
- 'tagnum', 'serial', '', '', '', '',
- 'tagname', 'varchar', '', $char_d, '', '',
- 'tagdesc', 'varchar', 'NULL', $char_d, '', '',
- 'tagcolor', 'varchar', 'NULL', 6, '', '',
- 'disabled', 'char', 'NULL', 1, '', '',
- ],
- 'primary_key' => 'tagnum',
- 'unique' => [], #[ [ 'tagname' ] ], #?
- 'index' => [ [ 'disabled' ] ],
- },
'cust_main_exemption' => {
'columns' => [
'cgp_rule_condition' => {
'columns' => [
'ruleconditionnum', 'serial', '', '', '', '',
- 'conditionname', 'varchar', '', $char_d, '', '',
+ 'condition', 'varchar', '', $char_d, '', '',
'op', 'varchar', 'NULL', $char_d, '', '',
'params', 'varchar', 'NULL', 255, '', '',
'rulenum', 'int', '', '', '', '',
'conn_sec', 'int', '', '', '0', '',
'min_charge', 'decimal', '', '10,5', '', '', #@money_type, '', '',
'sec_granularity', 'int', '', '', '', '',
- 'ratetimenum', 'int', 'NULL', '', '', '',
#time period (link to table of periods)?
'classnum', 'int', 'NULL', '', '', '',
],
'index' => [ [ 'countrycode' ], [ 'npa' ], [ 'regionnum' ] ],
},
- 'rate_time' => {
- 'columns' => [
- 'ratetimenum', 'serial', '', '', '', '',
- 'ratetimename', 'varchar', '', $char_d, '', '',
- ],
- 'primary_key' => 'ratetimenum',
- 'unique' => [],
- 'index' => [],
- },
-
- 'rate_time_interval' => {
- 'columns' => [
- 'intervalnum', 'serial', '', '', '', '',
- 'stime', 'int', '', '', '', '',
- 'etime', 'int', '', '', '', '',
- 'ratetimenum', 'int', '', '', '', '',
- ],
- 'primary_key' => 'intervalnum',
- 'unique' => [],
- 'index' => [],
- },
-
'usage_class' => {
'columns' => [
'classnum', 'serial', '', '', '', '',
'svc_pbx' => {
'columns' => [
- 'svcnum', 'int', '', '', '', '',
- 'id', 'int', 'NULL', '', '', '',
- 'title', 'varchar', 'NULL', $char_d, '', '',
- 'max_extensions', 'int', 'NULL', '', '', '',
- 'max_simultaneous', 'int', 'NULL', '', '', '',
+ 'svcnum', 'int', '', '', '', '',
+ 'id', 'int', 'NULL', '', '', '',
+ 'title', 'varchar', 'NULL', $char_d, '', '',
+ 'max_extensions', 'int', 'NULL', '', '', '',
],
'primary_key' => 'svcnum',
'unique' => [],
'index' => [['listnum'],['svcnum'],['contactemailnum'],['email']],
},
- 'bill_batch' => {
- 'columns' => [
- 'batchnum', 'serial', '', '', '', '',
- 'status', 'char', 'NULL','1', '', '',
- 'pdf', 'blob', 'NULL', '', '', '',
- ],
- 'primary_key' => 'batchnum',
- 'unique' => [],
- 'index' => [],
- },
-
- 'cust_bill_batch' => {
- 'columns' => [
- 'billbatchnum', 'serial', '', '', '', '',
- 'batchnum', 'int', '', '', '', '',
- 'invnum', 'int', '', '', '', '',
- ],
- 'primary_key' => 'billbatchnum',
- 'unique' => [],
- 'index' => [ [ 'batchnum' ], [ 'invnum' ] ],
- },
-
- 'cust_bill_batch_option' => {
- 'columns' => [
- 'optionnum', 'serial', '', '', '', '',
- 'billbatchnum', 'int', '', '', '', '',
- 'optionname', 'varchar', '', $char_d, '', '',
- 'optionvalue', 'text', 'NULL', '', '', '',
- ],
- 'primary_key' => 'optionnum',
- 'unique' => [],
- 'index' => [ [ 'billbatchnum' ], [ 'optionname' ] ],
- },
-
- 'msg_template' => {
- 'columns' => [
- 'msgnum', 'serial', '', '', '', '',
- 'msgname', 'varchar', '', $char_d, '', '',
- 'agentnum', 'int', 'NULL', '', '', '',
- 'subject', 'varchar', 'NULL', 512, '', '',
- 'mime_type', 'varchar', '', $char_d, '', '',
- 'body', 'blob', 'NULL', '', '', '',
- 'disabled', 'char', 'NULL', 1, '', '',
- 'from_addr', 'varchar', 'NULL', 255, '', '',
- ],
- 'primary_key' => 'msgnum',
- 'unique' => [ ['msgname', 'mime_type'] ],
- 'index' => [ ['agentnum'], ]
- },
-
-
# name type nullability length default local
use FS::UID qw(getotaker);
use FS::Record qw(qsearchs);
use FS::queue;
-use FS::CGI qw(rooturl);
$DEBUG = 0;
}
}
$param{CurrentUser} = getotaker();
- $param{RootURL} = rooturl($self->{cgi}->self_url);
warn "FS::UI::Web::start_job\n".
join('', map {
if ( ref($param{$_}) ) {
}
my @return;
- if ( $job && $job->status ne 'failed' && $job->status ne 'done' ) {
+ if ( $job && $job->status ne 'failed' ) {
my ($progress, $action) = split ',', $job->statustext, 2;
$action ||= 'Server processing job';
@return = ( 'progress', $progress, $action );
} elsif ( !$job ) { #handle job gone case : job successful
# so close popup, redirect parent window...
@return = ( 'complete' );
- } elsif ( $job->status eq 'done' ) {
- @return = ( 'done', $job->statustext, '' );
} else {
@return = ( 'error', $job ? $job->statustext : $jobnum );
}
use strict;
use vars qw(@EXPORT_OK @ISA);
use Exporter;
-use HTML::Entities;
use FS::Msgcat;
use FS::Record qw(qsearchs);
use FS::cust_main;
$html .= 'Customer #<B>'. $cust_main->display_custnum. '</B></A>'.
' - <B><FONT COLOR="#'. $cust_main->statuscolor. '">'.
- ucfirst($cust_main->status). '</FONT></B>';
-
- my @part_tag = $cust_main->part_tag;
- if ( @part_tag ) {
- $html .= '<TABLE>';
- foreach my $part_tag ( @part_tag ) {
- $html .= '<TR><TD>'.
- '<FONT '. ( length($part_tag->tagcolor)
- ? 'STYLE="background-color:#'.$part_tag->tagcolor.'"'
- : ''
- ).
- '>'.
- encode_entities($part_tag->tagname.': '. $part_tag->tagdesc).
- '</FONT>'.
- '</TD></TR>';
- }
- $html .= '</TABLE>';
- }
-
- $html .=
+ ucfirst($cust_main->status). '</FONT></B>'.
ntable('#e8e8e8'). '<TR><TD VALIGN="top">'. ntable("#cccccc",2).
'<TR><TD ALIGN="right" VALIGN="top">Billing<BR>Address</TD><TD BGCOLOR="#ffffff">'.
$cust_main->getfield('last'). ', '. $cust_main->first. '<BR>';
my $data = upgrade_data(%opt);
- my $oldAutoCommit = $FS::UID::AutoCommit;
- local $FS::UID::AutoCommit = 0;
- local $FS::UID::AutoCommit = 0;
-
foreach my $table ( keys %$data ) {
my $class = "FS::$table";
my $start = time;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ $FS::UID::AutoCommit = 0;
+
$class->_upgrade_data(%opt);
if ( $oldAutoCommit ) {
- warn " committing\n";
dbh->commit or die dbh->errstr;
}
'cust_refund' => [],
'banned_pay' => [],
- #default namespace
- 'payment_gateway' => [],
-
- #migrate to templates
- 'msg_template' => [],
-
;
\%hash;
my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
my $group = "UserName";
$group .= ",Realm"
- if ref($part_export) =~ /withdomain/
- || $dbh->{Driver}->{Name} =~ /^Pg/; #hmm
+ if ( ref($part_export) =~ /withdomain/ );
my $sth_alter = $dbh->prepare(
"ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL"
$sth_update->execute or die $errmsg.$sth_update->errstr;
} else {
my $error = $sth_alter->errstr;
- warn $errmsg.$error
- unless $error =~ /Duplicate column name/i #mysql
- || $error =~ /already exists/i; #Pg
-;
+ warn $errmsg.$error unless $error =~ /Duplicate column name/i;
}
} else {
my $error = $dbh->errstr;
if ( $sth_index ) {
unless ( $sth_index->execute ) {
my $error = $sth_index->errstr;
- warn $errmsg.$error
- unless $error =~ /Duplicate key name/i #mysql
- || $error =~ /already exists/i; #Pg
+ warn $errmsg.$error unless $error =~ /Duplicate key name/i;
}
} else {
my $error = $dbh->errstr;
- warn $errmsg.$error. ' (preparing statement)';#unless $error =~ /exists/i;
+ warn $errmsg.$error; #unless $error =~ /exists/i;
}
- my $times = ($dbh->{Driver}->{Name} =~ /^mysql/)
- ? ' AcctStartTime != 0 AND AcctStopTime != 0 '
- : ' AcctStartTime IS NOT NULL AND AcctStopTime IS NOT NULL ';
-
my $sth = $dbh->prepare("SELECT UserName,
Realm,
$str2time max(AcctStartTime)),
$str2time max(AcctStopTime))
FROM radacct
WHERE FreesideStatus = 'done'
- AND $times
+ AND AcctStartTime != 0
+ AND AcctStopTime != 0
GROUP BY $group
")
or die $errmsg.$dbh->errstr;
- package FS::XMLRPC;
+package FS::XMLRPC;
use strict;
-use vars qw( $DEBUG );
+use vars qw( @ISA $DEBUG );
use Frontier::RPC2;
# Instead of 'use'ing freeside modules on the fly below, just preload them now.
use FS::Record;
use FS::cust_main;
-use FS::Maestro;
-
use Data::Dumper;
+@ISA = qw( );
+
$DEBUG = 0;
=head1 NAME
}
- if ( scalar(@result) == 1 && ref($result[0]) eq 'HASH' ) {
- return $result[0];
- } elsif (grep { UNIVERSAL::can($_, 'hashref') ? 0 : 1 } @result) {
+ warn Dumper(@result) if $DEBUG;
+
+ if (grep { UNIVERSAL::can($_, 'hashref') ? 0 : 1 } @result) {
#warn "FS::XMLRPC: One or more objects returned from '${fssub}' doesn't " .
# "support the 'hashref' method.";
return [ $FS::VERSION ];
} # else...
- warn "Unhandled XMLRPC request '${method_name}'";
- return {};
+ warn "Unhandle XMLRPC request '${method_name}'";
+ return [];
}
package FS::access_user;
use strict;
-use base qw( FS::m2m_Common FS::option_Common );
-use vars qw( $DEBUG $me $conf $htpasswd_file );
+use vars qw( @ISA $DEBUG $me $conf $htpasswd_file );
use FS::UID;
use FS::Conf;
use FS::Record qw( qsearch qsearchs dbh );
+use FS::m2m_Common;
+use FS::option_Common;
use FS::access_user_pref;
use FS::access_usergroup;
use FS::agent;
use FS::cust_main;
+@ISA = qw( FS::m2m_Common FS::option_Common FS::Record );
+#@ISA = qw( FS::m2m_Common FS::option_Common );
+
$DEBUG = 0;
$me = '[FS::access_user]';
Optional table name in which agentnum is being checked. Sometimes required to
resolve 'column reference "agentnum" is ambiguous' errors.
-=item viewall_right
-
-All agents will be viewable if the current user has the provided access right.
-Defaults to 'View customers of all agents'.
-
=back
=cut
my $agentnum = $opt{'table'} ? $opt{'table'}.'.agentnum' : 'agentnum';
- my @or = ();
-
- my $viewall_right = $opt{'viewall_right'} || 'View customers of all agents';
- if ( $self->access_right($viewall_right) ) {
- push @or, "$agentnum IS NOT NULL";
- } else {
- push @or, "$agentnum IN (". join(',', $self->agentnums). ')';
- }
+# my @agentnums = map { "$agentnum = $_" } $self->agentnums;
+ my @agentnums = ();
+ push @agentnums, "$agentnum IN (". join(',', $self->agentnums). ')';
- push @or, "$agentnum IS NULL"
+ push @agentnums, "$agentnum IS NULL"
if $opt{'null'}
|| ( $opt{'null_right'} && $self->access_right($opt{'null_right'}) );
- return ' 1 = 0 ' unless scalar(@or);
- '( '. join( ' OR ', @or ). ' )';
+ return ' 1 = 0 ' unless scalar(@agentnums);
+ '( '. join( ' OR ', @agentnums ). ' )';
}
$sth->fetchrow_arrayref->[0];
}
-=item agents [ HASHREF | OPTION => VALUE ... ]
+=item agents
Returns the list of agents this user can view (via group membership), as
-FS::agent objects. Accepts the same options as the agentnums_sql method.
+FS::agent objects.
=cut
qsearch({
'table' => 'agent',
'hashref' => { disabled=>'' },
- 'extra_sql' => ' AND '. $self->agentnums_sql(@_),
+ 'extra_sql' => ' AND '. $self->agentnums_sql,
});
}
cardtype => '',
taxclass => '', } );
- my $payment_gateway;
- my $conf = new FS::Conf;
+ my $payment_gateway = new FS::payment_gateway;
if ( $override ) { #use a payment gateway override
$payment_gateway = $override->payment_gateway;
- $payment_gateway->gateway_namespace('Business::OnlinePayment')
- unless $payment_gateway->gateway_name;
-
} else { #use the standard settings from the config
-
# the standard settings from the config could be moved to a null agent
# agent_payment_gateway referenced payment_gateway
+ my $conf = new FS::Conf;
unless ( $conf->exists('business-onlinepayment') ) {
if ( $options{'nofatal'} ) {
return '';
"did you set the business-onlinepayment configuration value?\n"
unless $processor;
- $payment_gateway = new FS::payment_gateway;
-
$payment_gateway->gateway_namespace( $conf->config('business-onlinepayment-namespace') ||
'Business::OnlinePayment');
$payment_gateway->gateway_module($processor);
}
- unless ( $payment_gateway->gateway_namespace ) {
- $payment_gateway->gateway_namespace(
- scalar($conf->config('business-onlinepayment-namespace'))
- || 'Business::OnlinePayment'
- );
- }
-
$payment_gateway;
}
+++ /dev/null
-package FS::bill_batch;
-
-use strict;
-use vars qw( @ISA $me $DEBUG );
-use FS::Record qw( qsearch qsearchs dbh );
-use FS::cust_bill_batch;
-
-@ISA = qw( FS::Record );
-$me = '[ FS::bill_batch ]';
-$DEBUG=0;
-
-sub table { 'bill_batch' }
-
-sub nohistory_fields { 'pdf' }
-
-=head1 NAME
-
-FS::bill_batch - Object methods for bill_batch records
-
-=head1 SYNOPSIS
-
- use FS::bill_batch;
-
- $open_batch = FS::bill_batch->get_open_batch;
-
- my $pdf = $open_batch->print_pdf;
-
- $error = $open_batch->close;
-
-=head1 DESCRIPTION
-
-An FS::bill_batch object represents a batch of invoices. FS::bill_batch
-inherits from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item batchnum - primary key
-
-=item status - either 'O' (open) or 'R' (resolved/closed).
-
-=item pdf - blob field for temporarily storing the invoice as a PDF.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item print_pdf
-
-Typeset the entire batch as a PDF file. Returns the PDF as a string.
-
-=cut
-
-sub print_pdf {
- eval 'use CAM::PDF';
- warn "Failed to load CAM::PDF: '$@'\n" if $@;
-
- my $self = shift;
- my $job = shift;
- $job->update_statustext(0) if $job;
- my @invoices = sort { $a->invnum <=> $b->invnum }
- qsearch('cust_bill_batch', { batchnum => $self->batchnum });
- return "No invoices in batch ".$self->batchnum.'.' if !@invoices;
-
- my $pdf_out;
- my $num = 0;
- foreach my $invoice (@invoices) {
- my $part = $invoice->cust_bill->print_pdf({$invoice->options});
- die 'Failed creating PDF from invoice '.$invoice->invnum.'\n' if !$part;
-
- if($pdf_out) {
- $pdf_out->appendPDF(CAM::PDF->new($part));
- }
- else {
- $pdf_out = CAM::PDF->new($part);
- }
- if($job) {
- # update progressbar
- $num++;
- my $error = $job->update_statustext(int(100 * $num/scalar(@invoices)));
- die $error if $error;
- }
- }
-
- return $pdf_out->toPDF;
-}
-
-=item close
-
-Set the status of the batch to 'R' (resolved).
-
-=cut
-
-sub close {
- my $self = shift;
- $self->status('R');
- return $self->replace;
-}
-
-=back
-
-=head1 CLASS METHODS
-
-=item get_open_batch
-
-Returns the currently open batch. There should only be one at a time.
-
-=cut
-
-sub get_open_batch {
- my $class = shift;
- my $batch = qsearchs('bill_batch', { status => 'O' });
- return $batch if $batch;
- $batch = FS::bill_batch->new({status => 'O'});
- my $error = $batch->insert;
- die $error if $error;
- return $batch;
-}
-
-use Storable 'thaw';
-use Data::Dumper;
-use MIME::Base64;
-
-sub process_print_pdf {
- my $job = shift;
- my $param = thaw(decode_base64(shift));
- warn Dumper($param) if $DEBUG;
- die "no batchnum specified!\n" if ! exists($param->{batchnum});
- my $batch = FS::bill_batch->by_key($param->{batchnum});
- die "batch '$param->{batchnum}' not found!\n" if !$batch;
-
- my $pdf = $batch->print_pdf($job);
- $batch->pdf($pdf);
- my $error = $batch->replace;
- die $error if $error;
-}
-
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
# ;
# return $error if $error;
- for my $f ( grep { $self->$_ =~ /[a-z ]/i } qw( startdate enddate ) ) {
- $self->$f( str2time($self->$f) );
- }
-
$self->calldate( $self->startdate_sql )
if !$self->calldate && $self->startdate;
$self->SUPER::check;
}
-=item is_tollfree [ COLUMN ]
-
-Returns true when the cdr represents a toll free number and false otherwise.
+=item is_tollfree
-By default, inspects the dst field, but an optional column name can be passed
-to inspect other field.
+ Returns true when the cdr represents a toll free number and false otherwise.
=cut
sub is_tollfree {
my $self = shift;
- my $field = scalar(@_) ? shift : 'dst';
- ( $self->$field() =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0;
+ ( $self->dst =~ /^(\+?1)?8(8|([02-7])\3)/ ) ? 1 : 0;
}
=item set_charged_party
if $conf->exists('cdr-charged_party-accountcode-trim_leading_0s');
$self->charged_party( $charged_party );
- } elsif ( $conf->exists('cdr-charged_party-field') ) {
-
- my $field = $conf->config('cdr-charged_party-field');
- $self->charged_party( $self->$field() );
-
} else {
if ( $self->is_tollfree ) {
},
);
-my %export_formats = ();
-sub export_formats {
- #my $self = shift;
-
- return %export_formats if keys %export_formats;
-
- my $conf = new FS::Conf;
- my $date_format = $conf->config('date_format') || '%m/%d/%Y';
-
- my $duration_sub = sub {
- my($cdr, %opt) = @_;
- if ( $opt{minutes} ) {
- $opt{minutes}. ( $opt{granularity} ? 'm' : ' call' );
- } else {
- #config if anyone really wants decimal minutes back
- #sprintf('%.2fm', $cdr->billsec / 60 );
- int($cdr->billsec / 60).'m '. ($cdr->billsec % 60).'s';
- }
- };
-
- %export_formats = (
- 'simple' => [
- sub { time2str($date_format, shift->calldate_unix ) }, #DATE
- sub { time2str('%r', shift->calldate_unix ) }, #TIME
- 'userfield', #USER
- 'dst', #NUMBER_DIALED
- $duration_sub, #DURATION
- #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
- ],
- 'simple2' => [
- sub { time2str($date_format, shift->calldate_unix ) }, #DATE
- sub { time2str('%r', shift->calldate_unix ) }, #TIME
- #'userfield', #USER
- 'src', #called from
- 'dst', #NUMBER_DIALED
- $duration_sub, #DURATION
- #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
- ],
- 'default' => [
-
- #DATE
- sub { time2str($date_format, shift->calldate_unix ) },
- # #time2str("%Y %b %d - %r", $cdr->calldate_unix ),
-
- #TIME
- sub { time2str('%r', shift->calldate_unix ) },
- # time2str("%c", $cdr->calldate_unix), #XXX this should probably be a config option dropdown so they can select US vs- rest of world dates or whatnot
-
- #DEST ("Number")
- sub { my($cdr, %opt) = @_; $opt{pretty_dst} || $cdr->dst; },
-
- #REGIONNAME ("Destination")
- sub { my($cdr, %opt) = @_; $opt{dst_regionname}; },
-
- #DURATION
- $duration_sub,
-
- #PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; },
-
- ],
- );
- $export_formats{'source_default'} = [ 'src', @{ $export_formats{'default'} }, ];
- $export_formats{'accountcode_default'} =
- [ @{ $export_formats{'default'} }[0,1],
- 'accountcode',
- @{ $export_formats{'default'} }[2..5],
- ];
-
- %export_formats
-}
+my $duration_sub = sub {
+ my($cdr, %opt) = @_;
+ if ( $opt{minutes} ) {
+ $opt{minutes}. ( $opt{granularity} ? 'm' : ' call' );
+ } else {
+ #config if anyone really wants decimal minutes back
+ #sprintf('%.2fm', $cdr->billsec / 60 );
+ int($cdr->billsec / 60).'m '. ($cdr->billsec % 60).'s';
+ }
+};
+
+my %export_formats = (
+ 'simple' => [
+ sub { time2str('%D', shift->calldate_unix ) }, #DATE
+ sub { time2str('%r', shift->calldate_unix ) }, #TIME
+ 'userfield', #USER
+ 'dst', #NUMBER_DIALED
+ $duration_sub, #DURATION
+ #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ ],
+ 'simple2' => [
+ sub { time2str('%D', shift->calldate_unix ) }, #DATE
+ sub { time2str('%r', shift->calldate_unix ) }, #TIME
+ #'userfield', #USER
+ 'src', #called from
+ 'dst', #NUMBER_DIALED
+ $duration_sub, #DURATION
+ #sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ ],
+ 'default' => [
+
+ #DATE
+ sub { time2str('%D', shift->calldate_unix ) },
+ # #time2str("%Y %b %d - %r", $cdr->calldate_unix ),
+
+ #TIME
+ sub { time2str('%r', shift->calldate_unix ) },
+ # time2str("%c", $cdr->calldate_unix), #XXX this should probably be a config option dropdown so they can select US vs- rest of world dates or whatnot
+
+ #DEST ("Number")
+ sub { my($cdr, %opt) = @_; $opt{pretty_dst} || $cdr->dst; },
+
+ #REGIONNAME ("Destination")
+ sub { my($cdr, %opt) = @_; $opt{dst_regionname}; },
+
+ #DURATION
+ $duration_sub,
+
+ #PRICE
+ sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; },
+
+ ],
+);
+$export_formats{'source_default'} = [ 'src', @{ $export_formats{'default'} }, ];
+$export_formats{'accountcode_default'} =
+ [ @{ $export_formats{'default'} }[0,1],
+ 'accountcode',
+ @{ $export_formats{'default'} }[2..5],
+ ];
sub downstream_csv {
my( $self, %opt ) = @_;
my $format = $opt{'format'};
- my %formats = $self->export_formats;
- return "Unknown format $format" unless exists $formats{$format};
+ return "Unknown format $format" unless exists $export_formats{$format};
#my $conf = new FS::Conf;
#$opt{'money_char'} ||= $conf->config('money_char') || '$';
map {
ref($_) ? &{$_}($self, %opt) : $self->$_();
}
- @{ $formats{$format} };
+ @{ $export_formats{$format} };
my $status = $csv->combine(@columns);
die "FS::CDR: error combining ". $csv->error_input(). "into downstream CSV"
} elsif ( $date =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d+\.\d+)(\D|$)/ ) {
# broadsoft: 20081223201938.314
($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
- } elsif ( $date =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\d+(\D|$)/ ) {
- # Taqua OM: 20050422203450943
- ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
} elsif ( $date =~ /^\s*(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/ ) {
# WIP: 20100329121420
($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 );
{ map { $_ => $cdr_info{$_}->{'fixedlength_format'}; }
keys %cdr_info
},
-
- 'format_row_callbacks' => { map { $_ => $cdr_info{$_}->{'row_callback'}; }
- keys %cdr_info
- },
);
sub _import_options {
my %cdrbatchnum = ();
while (my $row = $sth->fetchrow_arrayref) {
-
- my $cdr_batch = qsearchs( 'cdr_batch', { 'cdrbatch' => $row->[0] } );
- unless ( $cdr_batch ) {
- $cdr_batch = new FS::cdr_batch { 'cdrbatch' => $row->[0] };
- my $error = $cdr_batch->insert;
- die $error if $error;
- }
-
+ my $cdr_batch = new FS::cdr_batch { 'cdrbatch' => $row->[0] };
+ my $error = $cdr_batch->insert;
+ die $error if $error;
$cdrbatchnum{$row->[0]} = $cdr_batch->cdrbatchnum;
}
+++ /dev/null
-package FS::cdr::taqua_om;
-
-use strict;
-use vars qw( %info );
-use base qw( FS::cdr::taqua );
-
-%info = (
- %FS::cdr::taqua::info,
- 'name' => 'Taqua OM',
- 'weight' => 132,
- 'header' => 0,
- 'sep_char' => ';',
- 'row_callback' => sub { my $row = shift;
- $row =~ s/^<\d+>\|[\da-f\|]+\|(\d+;)/$1/;
- $row;
- },
-);
-
-1;
# except that we assume that before all the fields mentioned in the
# spec, there's a counter field.
skip(4), # counter, id, APCSJursID, RecordType
- sub { my($cdr, $data, $conf, $param) = @_;
- $param->{skiprow} = 1 if $data == 1;
- $cdr->uniqueid($data);
- }, # CDRID; is 1 for line charge records
+ 'unique_id', # CDRID
skip(1), # AccountNumber; empty
'charged_party', # ServiceNumber
skip(1), # ServiceNumberType
'upstream_price', # ISPBuy
skip(2), # EUBuy, CDRFromCarrier
],
+# Need clarification on:
+# Values for RecordType, Jurisdiction, CompletionStatus, and ProviderClass
+# Do we care about the following:
+# AccountNumber, ServiceNumberType, CDRStatus
);
return $error;
}
- #conditions and actions not in yet
- #$error = $self->svc_export;
- #if ( $error ) {
- # $dbh->rollback if $oldAutoCommit;
- # return $error;
- #}
+ $error = $self->svc_export;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
return $error;
}
- #conditions and actions not in yet
- #$error = $new->svc_export;
- #if ( $error ) {
- # $dbh->rollback if $oldAutoCommit;
- # return $error;
- #}
+ $error = $new->svc_export;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
$self->SUPER::check;
}
-=item clone NEW_SVCNUM
-
-Clones this rule into an identical rule for the specified new service.
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-#should return the newly inserted rule instead? used in misc/clone-cgp_rule.html
-
-#i should probably be transactionalized so i'm all-or-nothing
-sub clone {
- my( $self, $svcnum ) = @_;
-
- my $new = $self->new( { $self->hash } );
- $new->rulenum('');
- $new->svcnum( $svcnum );
- my $error = $new->insert;
- return $error if $error;
-
- my @dup = $self->cgp_rule_condition;
- push @dup, $self->cgp_rule_action;
-
- foreach my $dup (@dup) {
- my $new_dup = $dup->new( { $dup->hash } );
- my $pk = $new_dup->primary_key;
- $new_dup->$pk('');
- $new_dup->rulenum( $new->rulenum );
-
- $error = $new_dup->insert;
- return $error if $error;
-
- }
-
- $error = $new->svc_export;
- return $error if $error;
-
- '';
-
-}
-
=item cust_svc
=cut
primary key
-=item conditionname
+=item condition
condition
my $error =
$self->ut_numbern('ruleconditionnum')
- || $self->ut_text('conditionname')
+ || $self->ut_text('condition')
|| $self->ut_textn('op')
|| $self->ut_textn('params')
|| $self->ut_foreign_key('rulenum', 'cgp_rule', 'rulenum')
=item arrayref
-Returns an array reference of the conditionname, op and params fields.
+Returns an array reference of the condition, op and params fields.
=cut
sub arrayref {
my $self = shift;
- [ map $self->$_, qw( conditionname op params ) ];
+ [ map $self->$_, qw( condition op params ) ];
}
=back
use FS::cust_bill_pay_batch;
use FS::part_bill_event;
use FS::payby;
-use FS::bill_batch;
-use FS::cust_bill_batch;
@ISA = qw( FS::cust_main_Mixin FS::Record );
'notice_name' => $notice_name,
);
- if($conf->exists('invoice_print_pdf')) {
- # Add the invoice to the current batch.
- $self->batch_invoice(\%opt);
- }
- else {
- do_print $self->lpr_data(\%opt);
- }
+ do_print $self->lpr_data(\%opt);
}
=item fax_invoice HASHREF | [ TEMPLATE ]
}
-=item batch_invoice [ HASHREF ]
-
-Place this invoice into the open batch (see C<FS::bill_batch>). If there
-isn't an open batch, one will be created.
-
-=cut
-
-sub batch_invoice {
- my ($self, $opt) = @_;
- my $batch = FS::bill_batch->get_open_batch;
- my $cust_bill_batch = FS::cust_bill_batch->new({
- batchnum => $batch->batchnum,
- invnum => $self->invnum,
- });
- return $cust_bill_batch->insert($opt);
-}
-
=item ftp_invoice [ TEMPLATENAME ]
Sends this invoice data via FTP.
}
- my $agentnum = $self->cust_main->agentnum;
-
my %invoice_data = (
#invoice from info
- 'company_name' => scalar( $conf->config('company_name', $agentnum) ),
- 'company_address' => join("\n", $conf->config('company_address', $agentnum) ). "\n",
+ 'company_name' => scalar( $conf->config('company_name', $self->cust_main->agentnum) ),
+ 'company_address' => join("\n", $conf->config('company_address', $self->cust_main->agentnum) ). "\n",
'returnaddress' => $returnaddress,
'agent' => &$escape_function($cust_main->agent->agent),
'smallerfooter' => $conf->exists('invoice-smallerfooter'),
'balance_due_below_line' => $conf->exists('balance_due_below_line'),
- #layout info -- would be fancy to calc some of this and bury the template
- # here in the code
- 'topmargin' => scalar($conf->config('invoice_latextopmargin', $agentnum)),
- 'headsep' => scalar($conf->config('invoice_latexheadsep', $agentnum)),
- 'textheight' => scalar($conf->config('invoice_latextextheight', $agentnum)),
- 'extracouponspace' => scalar($conf->config('invoice_latexextracouponspace', $agentnum)),
- 'couponfootsep' => scalar($conf->config('invoice_latexcouponfootsep', $agentnum)),
- 'verticalreturnaddress' => $conf->exists('invoice_latexverticalreturnaddress', $agentnum),
- 'addresssep' => scalar($conf->config('invoice_latexaddresssep', $agentnum)),
- 'amountenclosedsep' => scalar($conf->config('invoice_latexcouponamountenclosedsep', $agentnum)),
- 'coupontoaddresssep' => scalar($conf->config('invoice_latexcoupontoaddresssep', $agentnum)),
- 'addcompanytoaddress' => $conf->exists('invoice_latexcouponaddcompanytoaddress', $agentnum),
-
# better hang on to conf_dir for a while (for old templates)
'conf_dir' => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
$invoice_data{'previous_balance'} = sprintf("%.2f", $pr_total);
$invoice_data{'balance'} = sprintf("%.2f", $balance_due);
+ my $agentnum = $self->cust_main->agentnum;
+
my $summarypage = '';
if ( $conf->exists('invoice_usesummary', $agentnum) ) {
$summarypage = 1;
foreach my $section (@sections, @$late_sections) {
- # begin some normalization
- $section->{'subtotal'} = $section->{'amount'}
- if $multisection
- && !exists($section->{subtotal})
- && exists($section->{amount});
-
$invoice_data{finance_amount} = sprintf('%.2f', $section->{'subtotal'} )
if ( $invoice_data{finance_section} &&
$section->{'description'} eq $invoice_data{finance_section} );
sprintf('%.2f', $section->{'subtotal'})
if $multisection;
- # continue some normalization
+ # begin some normalization
$section->{'amount'} = $section->{'subtotal'}
if $multisection;
'fields' => [
sub { shift->{description} },
sub { shift->{quantity} },
- sub { my($href, %opt) = @_;
- ($opt{dollar} || ''). $href->{amount};
- },
+ sub { shift->{amount} },
],
'align' => [ qw( l r r ) ],
'span' => [ qw( 5 1 1 ) ], # unitprices?
my ( $f, $prefix, $suffix, $separator, $column ) =
_condensed_generator_defaults($format);
- my $money_char = '$';
if ($format eq 'latex') {
$prefix = "\\hline\n\\multicolumn{1}{c}{\\rule{0pt}{2.5ex}~} &\n";
$suffix = '\\\\';
sub { my ($d,$a,$s,$w) = @_;
return "\\multicolumn{$s}{$a}{\\makebox[$w][$a]{\\textbf{$d}}}";
};
- $money_char = '\\dollar';
}elsif ( $format eq 'html' ) {
$prefix = '"><td align="center"></td>';
$suffix = '';
sub { my ($d,$a,$s,$w) = @_;
return qq!<td align="$html_align{$a}">$d</td>!;
};
- #$money_char = $conf->config('money_char') || '$';
- $money_char = ''; # this is madness
}
sub {
- #my @args = @_;
- my $href = shift;
+ my @args = @_;
my @result = ();
foreach (my $i = 0; $f->{label}->[$i]; $i++) {
- my $dollar = '';
- $dollar = $money_char if $i == scalar(@{$f->{label}})-1;
- push @result,
- &{$column}( &{$f->{fields}->[$i]}($href, 'dollar' => $dollar),
- map { $f->{$_}->[$i] } qw(align span width)
- );
+ push @result, &{$column}( &{$f->{fields}->[$i]}(@args),
+ map { $f->{$_}->[$i] } qw(align span width)
+ );
}
$prefix. join( $separator, @result ). $suffix;
foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
next unless $cust_bill_pkg->pkgnum > 0;
- my @header = $cust_bill_pkg->details_header;
- next unless scalar(@header);
-
foreach my $detail ( $cust_bill_pkg->cust_bill_pkg_detail ) {
my $phonenum = $detail->phonenum;
'duration' => 0,
'sort_weight' => $usage_class{$detail->classnum}->weight,
'phonenum' => $phonenum,
- 'header' => [ @header ],
};
$sections{"$phonenum $line"}{amount} += $amount; #subtotal
$sections{"$phonenum $line"}{calls}++;
my %sectionmap = ();
my $simple = new FS::usage_class { format => 'simple' }; #bleh
+ my $usage_simple = new FS::usage_class { format => 'usage_simple' }; #bleh
foreach ( keys %sections ) {
- my @header = @{ $sections{$_}{header} || [] };
- my $usage_simple =
- new FS::usage_class { format => 'usage_'. (scalar(@header) || 6). 'col' };
my $summary = $sections{$_}{sort_weight} < 0 ? 1 : 0;
my $usage_class = $summary ? $simple : $usage_simple;
my $ending = $summary ? ' usage charges' : '';
- my %gen_opt = ();
- unless ($summary) {
- $gen_opt{label} = [ map{ &{$escape}($_) } @header ];
- }
$sectionmap{$_} = { 'description' => &{$escape}($_. $ending),
'amount' => $sections{$_}{amount}, #subtotal
'calls' => $sections{$_}{calls},
'sort_weight' => $sections{$_}{sort_weight},
'post_total' => $summary, #inspire pagebreak
(
- ( map { $_ => $usage_class->$_($format, %gen_opt) }
+ ( map { $_ => $usage_class->$_($format) }
qw( description_generator
header_generator
total_generator
}
sub _taxsort {
- return 0 unless $a->itemdesc cmp $b->itemdesc;
- return -1 if $b->itemdesc eq 'Tax';
- return 1 if $a->itemdesc eq 'Tax';
- return -1 if $b->itemdesc eq 'Other surcharges';
- return 1 if $a->itemdesc eq 'Other surcharges';
- $a->itemdesc cmp $b->itemdesc;
+ return 0 unless $a cmp $b;
+ return -1 if $b eq 'Tax';
+ return 1 if $a eq 'Tax';
+ return -1 if $b eq 'Other surcharges';
+ return 1 if $a eq 'Other surcharges';
+ $a cmp $b;
}
sub _items_tax {
foreach my $cust_svc ( $cust_pkg->cust_svc ) {
my $svc_x = $cust_svc->svc_x;
- my @part_export = grep { $_->can('_export_insert_on_payment') }
+ my @part_export = grep { $_->can('export_insert_on_payment') }
$cust_svc->part_svc->part_export;
- foreach my $part_export ( @part_export ) {
+ foreach my $part_export ( $cust_svc->part_svc->part_export ) {
$error = $part_export->_export_insert_on_payment($svc_x);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
+++ /dev/null
-package FS::cust_bill_batch;
-
-use strict;
-use vars qw( @ISA $me $DEBUG );
-use FS::Record qw( qsearch qsearchs dbh );
-
-@ISA = qw( FS::option_Common );
-$me = '[ FS::cust_bill_batch ]';
-$DEBUG=0;
-
-sub table { 'cust_bill_batch' }
-
-=head1 NAME
-
-FS::cust_bill_batch - Object methods for cust_bill_batch records
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_batch object represents the inclusion of an invoice in a
-processing batch. FS::cust_bill_batch inherits from FS::option_Common. The
-following fields are currently supported:
-
-=over 4
-
-=item billbatchnum - primary key
-
-=item invnum - invoice number (see C<FS::cust_bill>)
-
-=item batchnum - batchn number (see C<FS::bill_batch>)
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item bill_batch
-
-Returns the C<FS::bill_batch> object.
-
-=cut
-
-sub bill_batch {
- my $self = shift;
- FS::bill_batch->by_key($self->batchnum);
-}
-
-=item cust_bill
-
-Returns the C<FS::cust_bill> object.
-
-=cut
-
-sub cust_bill {
- my $self = shift;
- FS::cust_bill->by_key($self->invnum);
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
+++ /dev/null
-package FS::cust_bill_batch_option;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
-
-@ISA = qw(FS::Record);
-
-=head1 NAME
-
-FS::cust_bill_batch_option - Object methods for cust_bill_batch_option records
-
-=head1 SYNOPSIS
-
- use FS::cust_bill_batch_option;
-
- $record = new FS::cust_bill_batch_option \%hash;
- $record = new FS::cust_bill_batch_option { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_batch_option object represents an option key and value for
-an invoice batch entry. FS::cust_bill_batch_option inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item optionnum - primary key
-
-=item billbatchnum -
-
-=item optionname -
-
-=item optionvalue -
-
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new option. To add the option to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'cust_bill_batch_option'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid option. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('optionnum')
- || $self->ut_foreign_key('billbatchnum', 'cust_bill_batch', 'billbatchnum')
- || $self->ut_text('optionname')
- || $self->ut_textn('optionvalue')
- ;
- return $error if $error;
-
- $self->SUPER::check;
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
#qsearch ( 'cust_bill_pkg_detail', { 'lineitemnum' => $self->lineitemnum });
}
-=item details_header [ OPTION => VALUE ... ]
-
-Returns a list representing an invoice line item detail header, if any.
-This relies on the behavior of voip_cdr in that it expects the header
-to be the first CSV formatted detail (as is expected by invoice generation
-routines). Returns the empty list otherwise.
-
-=cut
-
-sub details_header {
- my $self = shift;
- return '' unless defined dbdef->table('cust_bill_pkg_detail');
-
- eval "use Text::CSV_XS;";
- die $@ if $@;
- my $csv = new Text::CSV_XS;
-
- my @detail =
- qsearch ({ 'table' => 'cust_bill_pkg_detail',
- 'hashref' => { 'billpkgnum' => $self->billpkgnum,
- 'format' => 'C',
- },
- 'order_by' => 'ORDER BY detailnum LIMIT 1',
- });
- return() unless scalar(@detail);
- $csv->parse($detail[0]->detail) or return ();
- $csv->fields;
-}
-
=item desc
Returns a description for this line item. For typical line items, this is the
use strict;
use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::Record );
-use vars qw( $conf $unsuspendauto $me $DEBUG $otaker_upgrade_kludge );
+use vars qw( $conf $unsuspendauto $me $DEBUG );
use Date::Format;
use FS::UID qw( dbh getotaker );
use FS::Misc qw(send_email);
$me = '[ FS::cust_credit ]';
$DEBUG = 0;
-$otaker_upgrade_kludge = 0;
-
#ask FS::UID to run this stuff for us later
$FS::UID::callback{'FS::cust_credit'} = sub {
return "amount must be > 0 " if $self->amount <= 0;
return "amount must be greater or equal to amount applied"
- if $self->unapplied < 0 && ! $otaker_upgrade_kludge;
+ if $self->unapplied < 0;
return "Unknown customer"
unless qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
}
}
- local($otaker_upgrade_kludge) = 1;
$class->_upgrade_otaker(%opts);
}
my $cust_tax_exempt_pkg = new FS::cust_tax_exempt_pkg {
'billpkgnum' => $self->billpkgnum,
'creditbillpkgnum' => $self->creditbillpkgnum,
- 'amount' => sprintf('%.2f', 0-$amount),
+ 'amount' => 0-$amount,
map { $_ => $exemption->$_ } split(',', $groupby)
};
my $error = $cust_tax_exempt_pkg->insert;
" (". $part_event->action. ") $for\n"
if $DEBUG;
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+
my $error;
{
local $SIG{__DIE__}; # don't want Mason __DIE__ handler active
use Scalar::Util qw( blessed );
use List::Util qw( min );
use Time::Local qw(timelocal);
-use Storable qw(thaw);
-use MIME::Base64;
use Data::Dumper;
use Tie::IxHash;
use Digest::MD5 qw(md5_base64);
use FS::part_pkg_taxrate;
use FS::agent;
use FS::cust_main_invoice;
-use FS::cust_tag;
use FS::cust_credit_bill;
use FS::cust_bill_pay;
use FS::prepay_credit;
use FS::part_pkg;
use FS::part_event;
use FS::part_event_condition;
-use FS::part_export;
#use FS::cust_event;
use FS::type_pkgs;
use FS::payment_gateway;
@fuzzyfields = ( 'first', 'last', 'company', 'address1' );
@encrypted_fields = ('payinfo', 'paycvv');
-sub nohistory_fields { ('payinfo', 'paycvv'); }
+sub nohistory_fields { ('paycvv'); }
@paytypes = ('', 'Personal checking', 'Personal savings', 'Business checking', 'Business savings');
$self->invoicing_list( $invoicing_list );
}
- warn " setting customer tags\n"
- if $DEBUG > 1;
-
- foreach my $tagnum ( @{ $self->tagnum || [] } ) {
- my $cust_tag = new FS::cust_tag { 'tagnum' => $tagnum,
- 'custnum' => $self->custnum };
- my $error = $cust_tag->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- if ( $invoicing_list ) {
- $error = $self->check_invoicing_list( $invoicing_list );
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- #return "checking invoicing_list (transaction rolled back): $error";
- return $error;
- }
- $self->invoicing_list( $invoicing_list );
- }
-
-
warn " setting cust_main_exemption\n"
if $DEBUG > 1;
}
}
- # cust_main exports!
- warn " exporting\n" if $DEBUG > 1;
-
- my $export_args = $options{'export_args'} || [];
-
- my @part_export =
- map qsearch( 'part_export', {exportnum=>$_} ),
- $conf->config('cust_main-exports'); #, $agentnum
-
- foreach my $part_export ( @part_export ) {
- my $error = $part_export->export_insert($self, @$export_args);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "exporting to ". $part_export->exporttype.
- " (transaction rolled back): $error";
- }
- }
-
- #foreach my $depend_jobnum ( @$depend_jobnums ) {
- # warn "[$me] inserting dependancies on supplied job $depend_jobnum\n"
- # if $DEBUG;
- # foreach my $jobnum ( @jobnums ) {
- # my $queue = qsearchs('queue', { 'jobnum' => $jobnum } );
- # warn "[$me] inserting dependancy for job $jobnum on $depend_jobnum\n"
- # if $DEBUG;
- # my $error = $queue->depend_insert($depend_jobnum);
- # if ( $error ) {
- # $dbh->rollback if $oldAutoCommit;
- # return "error queuing job dependancy: $error";
- # }
- # }
- # }
- #
- #}
- #
- #if ( exists $options{'jobnums'} ) {
- # push @{ $options{'jobnums'} }, @jobnums;
- #}
-
warn " insert complete; committing transaction\n"
if $DEBUG > 1;
}
}
- foreach my $table (qw( cust_main_invoice cust_main_exemption cust_tag )) {
- foreach my $record ( qsearch( 'table', { 'custnum' => $self->custnum } ) ) {
- my $error = $record->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
+ foreach my $cust_main_invoice ( #(email invoice destinations, not invoices)
+ qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } )
+ ) {
+ my $error = $cust_main_invoice->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ foreach my $cust_main_exemption (
+ qsearch( 'cust_main_exemption', { 'custnum' => $self->custnum } )
+ ) {
+ my $error = $cust_main_exemption->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
}
return $error;
}
- # cust_main exports!
-
- #my $export_args = $options{'export_args'} || [];
-
- my @part_export =
- map qsearch( 'part_export', {exportnum=>$_} ),
- $conf->config('cust_main-exports'); #, $agentnum
-
- foreach my $part_export ( @part_export ) {
- my $error = $part_export->export_delete( $self ); #, @$export_args);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "exporting to ". $part_export->exporttype.
- " (transaction rolled back): $error";
- }
- }
-
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
$self->invoicing_list( $invoicing_list );
}
- if ( $self->exists('tagnum') ) { #so we don't delete these on edit by accident
-
- #this could be more efficient than deleting and re-inserting, if it matters
- foreach my $cust_tag (qsearch('cust_tag', {'custnum'=>$self->custnum} )) {
- my $error = $cust_tag->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
- foreach my $tagnum ( @{ $self->tagnum || [] } ) {
- my $cust_tag = new FS::cust_tag { 'tagnum' => $tagnum,
- 'custnum' => $self->custnum };
- my $error = $cust_tag->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- }
-
my %options = @param;
my $tax_exemption = delete $options{'tax_exemption'};
}
- if ( $self->payby =~ /^(CARD|CHEK|LECB)$/
- && ( ( $self->get('payinfo') ne $old->get('payinfo')
- && $self->get('payinfo') !~ /^99\d{14}$/
- )
- || grep { $self->get($_) ne $old->get($_) } qw(paydate payname)
- )
- )
- {
-
+ if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ &&
+ grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) {
# card/check/lec info has changed, want to retry realtime_ invoice events
my $error = $self->retry_realtime;
if ( $error ) {
}
}
- # cust_main exports!
-
- my $export_args = $options{'export_args'} || [];
-
- my @part_export =
- map qsearch( 'part_export', {exportnum=>$_} ),
- $conf->config('cust_main-exports'); #, $agentnum
-
- foreach my $part_export ( @part_export ) {
- my $error = $part_export->export_replace( $self, $old, @$export_args);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "exporting to ". $part_export->exporttype.
- " (transaction rolled back): $error";
- }
- }
-
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
# If it is encrypted and the private key is not availaible then we can't
# check the credit card.
- my $check_payinfo = ! $self->is_encrypted($self->payinfo);
+
+ my $check_payinfo = 1;
+
+ if ($self->is_encrypted($self->payinfo)) {
+ $check_payinfo = 0;
+ }
if ( $check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) {
or return gettext('invalid_card'); # . ": ". $self->payinfo;
return gettext('unknown_card_type')
- if $self->payinfo !~ /^99\d{14}$/ #token
- && cardtype($self->payinfo) eq "Unknown";
+ if cardtype($self->payinfo) eq "Unknown";
my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
if ( $ban ) {
return 1 if !$a_num_cust_svc && $b_num_cust_svc;
my @a_cust_svc = $a->cust_svc;
my @b_cust_svc = $b->cust_svc;
- return 0 if !scalar(@a_cust_svc) && !scalar(@b_cust_svc);
- return -1 if scalar(@a_cust_svc) && !scalar(@b_cust_svc);
- return 1 if !scalar(@a_cust_svc) && scalar(@b_cust_svc);
$a_cust_svc[0]->svc_x->label cmp $b_cust_svc[0]->svc_x->label;
}
qsearchs( 'agent', { 'agentnum' => $self->agentnum } );
}
-=item agent_name
-
-Returns the agent name (see L<FS::agent>) for this customer.
-
-=cut
-
-sub agent_name {
- my $self = shift;
- $self->agent->agent;
-}
-
-=item cust_tag
-
-Returns any tags associated with this customer, as FS::cust_tag objects,
-or an empty list if there are no tags.
-
-=cut
-
-sub cust_tag {
- my $self = shift;
- qsearch('cust_tag', { 'custnum' => $self->custnum } );
-}
-
-=item part_tag
-
-Returns any tags associated with this customer, as FS::part_tag objects,
-or an empty list if there are no tags.
-
-=cut
-
-sub part_tag {
- my $self = shift;
- map $_->part_tag, $self->cust_tag;
-}
-
-
=item cust_class
Returns the customer class, as an FS::cust_class object, or the empty string
Debugging level. Default is 0 (no debugging), or can be set to 1 (passed-in options), 2 (traces progress), 3 (more information), or 4 (include full search queries)
-=item job
-
-Optional FS::queue entry to receive status updates.
-
=back
Options are passed to the B<bill> and B<collect> methods verbatim, so all
#pre-printing invoices
$options{'actual_time'} ||= time;
- my $job = $options{'job'};
- $job->update_statustext('0,cleaning expired packages') if $job;
$error = $self->cancel_expired_pkgs( $options{actual_time} );
if ( $error ) {
$error = "Error expiring custnum ". $self->custnum. ": $error";
else { warn $error; }
}
- $job->update_statustext('20,billing packages') if $job;
$error = $self->bill( %options );
if ( $error ) {
$error = "Error billing custnum ". $self->custnum. ": $error";
else { warn $error; }
}
- $job->update_statustext('50,applying payments and credits') if $job;
$error = $self->apply_payments_and_credits;
if ( $error ) {
$error = "Error applying custnum ". $self->custnum. ": $error";
else { warn $error; }
}
- $job->update_statustext('70,running collection events') if $job;
unless ( $conf->exists('cancelled_cust-noevents')
&& ! $self->num_ncancelled_pkgs
) {
else { warn $error; }
}
}
- $job->update_statustext('100,finished') if $job;
'';
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- warn "$me acquiring lock on customer ". $self->custnum. "\n"
- if $DEBUG;
-
$self->select_for_update; #mutex
- warn "$me running pre-bill events for customer ". $self->custnum. "\n"
- if $DEBUG;
-
my $error = $self->do_cust_event(
'debug' => ( $options{'debug'} || 0 ),
'time' => $invoice_time,
return $error;
}
- warn "$me done running pre-bill events for customer ". $self->custnum. "\n"
- if $DEBUG;
-
#keep auto-charge and non-auto-charge line items separate
my @passes = ( '', 'no_auto' );
}
}
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
- #never want to roll back an event just because it returned an error
- local $FS::UID::AutoCommit = 1; #$oldAutoCommit;
-
- $self->do_cust_event(
+ my $error = $self->do_cust_event(
'debug' => ( $options{'debug'} || 0 ),
'time' => $invoice_time,
'check_freq' => $options{'check_freq'},
'stage' => 'collect',
);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
}
return $due_cust_event;
}
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
- #never want to roll back an event just because it or a different one
- # returned an error
- local $FS::UID::AutoCommit = 1; #$oldAutoCommit;
-
foreach my $cust_event ( @$due_cust_event ) {
#XXX lock event
unless ( $cust_event->test_conditions( 'time' => $time ) ) {
#don't leave stray "new/locked" records around
my $error = $cust_event->delete;
- return $error if $error;
+ if ( $error ) {
+ #gah, even with transactions
+ $dbh->commit if $oldAutoCommit; #well.
+ return $error;
+ }
next;
}
warn " running cust_event ". $cust_event->eventnum. "\n"
if $DEBUG > 1;
+
#if ( my $error = $cust_event->do_event(%options) ) { #XXX %options?
if ( my $error = $cust_event->do_event() ) {
#XXX wtf is this? figure out a proper dealio with return value
#from do_event
- return $error;
- }
+ # gah, even with transactions.
+ $dbh->commit if $oldAutoCommit; #well.
+ return $error;
+ }
}
}
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
$options->{payment_gateway}->gatewaynum
? $options->{payment_gateway}->options
: @{ $options->{payment_gateway}->get('options') };
-
}
sub _bop_defaults {
my ($self, $options) = @_;
my %content = ();
+ $content{address} = exists($options->{'address1'})
+ ? $options->{'address1'}
+ : $self->address1;
+ my $address2 = exists($options->{'address2'})
+ ? $options->{'address2'}
+ : $self->address2;
+ $content{address} .= ", ". $address2 if length($address2);
+
my $payip = exists($options->{'payip'}) ? $options->{'payip'} : $self->payip;
$content{customer_ip} = $payip if length($payip);
( $conf->exists('business-onlinepayment-email_customer')
|| $conf->exists('business-onlinepayment-email-override') );
- my ($payname, $payfirst, $paylast);
- if ( $options->{payname} && $options->{method} ne 'ECHECK' ) {
- ($payname = $options->{payname}) =~
- /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
- or return "Illegal payname $payname";
- ($payfirst, $paylast) = ($1, $2);
- } else {
- $payfirst = $self->getfield('first');
- $paylast = $self->getfield('last');
- $payname = "$payfirst $paylast";
- }
+ $content{payfirst} = $self->getfield('first');
+ $content{paylast} = $self->getfield('last');
- $content{last_name} = $paylast;
- $content{first_name} = $payfirst;
+ $content{account_name} = "$content{payfirst} $content{paylast}"
+ if $options->{method} eq 'ECHECK';
- $content{name} = $payname;
-
- $content{address} = exists($options->{'address1'})
- ? $options->{'address1'}
- : $self->address1;
- my $address2 = exists($options->{'address2'})
- ? $options->{'address2'}
- : $self->address2;
- $content{address} .= ", ". $address2 if length($address2);
+ $content{name} = $options->{payname};
+ $content{name} = $content{account_name} if exists($content{account_name});
$content{city} = exists($options->{city})
? $options->{city}
$content{country} = exists($options->{country})
? $options->{country}
: $self->country;
-
$content{referer} = 'http://cleanwhisker.420.am/'; #XXX fix referer :/
$content{phone} = $self->daytime || $self->night;
- \%content;
+ (%content);
}
my %bop_method2payby = (
# massage data
###
- my $bop_content = $self->_bop_content(\%options);
- return $bop_content unless ref($bop_content);
+ my (%bop_content) = $self->_bop_content(\%options);
+
+ if ( $options{method} ne 'ECHECK' ) {
+ $options{payname} =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
+ or return "Illegal payname $options{payname}";
+ ($bop_content{payfirst}, $bop_content{paylast}) = ($1, $2);
+ }
my @invoicing_list = $self->invoicing_list_emailonly;
if ( $conf->exists('emailinvoiceautoalways')
$content{account_type} = exists($options{'paytype'})
? uc($options{'paytype'}) || 'CHECKING'
: uc($self->getfield('paytype')) || 'CHECKING';
- $content{account_name} = $self->getfield('first'). ' '.
- $self->getfield('last');
-
$content{customer_org} = $self->company ? 'B' : 'I';
$content{state_id} = exists($options{'stateid'})
? $options{'stateid'}
'amount' => $options{amount},
#'invoice_number' => $options{'invnum'},
'customer_id' => $self->custnum,
- %$bop_content,
+ %bop_content,
'reference' => $cust_pay_pending->paypendingnum, #for now
'email' => $email,
%content, #after
my $BOP_TESTING_SUCCESS = 1;
unless ( $BOP_TESTING ) {
- $transaction->test_transaction(1)
- if $conf->exists('business-onlinepayment-test_transaction');
$transaction->submit();
} else {
if ( $BOP_TESTING_SUCCESS ) {
$capture->content( %capture );
- $capture->test_transaction(1)
- if $conf->exists('business-onlinepayment-test_transaction');
$capture->submit();
unless ( $capture->is_success ) {
}
###
- # Tokenize
- ###
-
-
- if ( $transaction->can('card_token') && $transaction->card_token ) {
-
- $self->card_token($transaction->card_token);
-
- if ( $options{'payinfo'} eq $self->payinfo ) {
- $self->payinfo($transaction->card_token);
- my $error = $self->replace;
- if ( $error ) {
- warn "WARNING: error storing token: $error, but proceeding anyway\n";
- }
- }
-
- }
-
- ###
# result handling
###
'paid' => $cust_pay_pending->paid,
'_date' => '',
'payby' => $cust_pay_pending->payby,
- 'payinfo' => $options{'payinfo'},
+ #'payinfo' => $payinfo,
'paybatch' => $paybatch,
'paydate' => $cust_pay_pending->paydate,
'pkgnum' => $cust_pay_pending->pkgnum,
&& ! grep { $transaction->error_message =~ /$_/ }
$conf->config('emaildecline-exclude')
) {
+ my @templ = $conf->config('declinetemplate');
+ my $template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", @templ ],
+ ) or return "($perror) can't create template: $Text::Template::ERROR";
+ $template->compile()
+ or return "($perror) can't compile template: $Text::Template::ERROR";
+
+ my $templ_hash = {
+ 'company_name' =>
+ scalar( $conf->config('company_name', $self->agentnum ) ),
+ 'company_address' =>
+ join("\n", $conf->config('company_address', $self->agentnum ) ),
+ 'error' => $transaction->error_message,
+ };
- # Send a decline alert to the customer.
- my $msgnum = $conf->config('decline_msgnum', $self->agentnum);
- my $error = '';
- if ( $msgnum ) {
- my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
- $error = $msg_template->send( 'cust_main' => $self );
- }
- else { #!$msgnum
-
- my @templ = $conf->config('declinetemplate');
- my $template = new Text::Template (
- TYPE => 'ARRAY',
- SOURCE => [ map "$_\n", @templ ],
- ) or return "($perror) can't create template: $Text::Template::ERROR";
- $template->compile()
- or return "($perror) can't compile template: $Text::Template::ERROR";
-
- my $templ_hash = {
- 'company_name' =>
- scalar( $conf->config('company_name', $self->agentnum ) ),
- 'company_address' =>
- join("\n", $conf->config('company_address', $self->agentnum ) ),
- 'error' => $transaction->error_message,
- };
-
- my $error = send_email(
- 'from' => $conf->config('invoice_from', $self->agentnum ),
- 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ],
- 'subject' => 'Your payment could not be processed',
- 'body' => [ $template->fill_in(HASH => $templ_hash) ],
- );
- }
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->agentnum ),
+ 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ],
+ 'subject' => 'Your payment could not be processed',
+ 'body' => [ $template->fill_in(HASH => $templ_hash) ],
+ );
$perror .= " (also received error sending decline notification: $error)"
if $error;
my $self = shift;
my %options = ();
- if (ref($_[0]) eq 'HASH') {
+ if (ref($_[0]) ne 'HASH') {
%options = %{$_[0]};
} else {
my $method = shift;
}
}
$void->content( 'action' => 'void', %content );
- $void->test_transaction(1)
- if $conf->exists('business-onlinepayment-test_transaction');
$void->submit();
if ( $void->is_success ) {
my $error = $cust_pay->void($options{'reason'});
);
warn join('', map { " $_ => $sub_content{$_}\n" } keys %sub_content )
if $DEBUG > 1;
- $refund->test_transaction(1)
- if $conf->exists('business-onlinepayment-test_transaction');
$refund->submit();
return "$processor error: ". $refund->error_message
my $self = shift;
my $time = shift;
- my $custnum = $self->custnum;
-
- my $owed_sql = FS::cust_bill->owed_sql;
-
- my $sql = "
- SELECT SUM($owed_sql) FROM cust_bill
- WHERE custnum = $custnum
- AND _date <= $time
- ";
+# my $custnum = $self->custnum;
+#
+# my $owed_sql = FS::cust_bill->owed_sql;
+#
+# my $sql = "
+# SELECT SUM($owed_sql) FROM cust_bill
+# WHERE custnum = $custnum
+# AND _date <= $time
+# ";
+#
+# my $sth = dbh->prepare($sql) or die dbh->errstr;
+# $sth->execute() or die $sth->errstr;
+#
+# return sprintf( '%.2f', $sth->fetchrow_arrayref->[0] );
- sprintf( "%.2f", $self->scalar_sql($sql) );
+ my $total_bill = 0;
+ foreach my $cust_bill (
+ grep { $_->_date <= $time }
+ qsearch('cust_bill', { 'custnum' => $self->custnum, } )
+ ) {
+ $total_bill += $cust_bill->owed;
+ }
+ sprintf( "%.2f", $total_bill );
}
sub total_unapplied_credits {
my $self = shift;
-
- my $custnum = $self->custnum;
-
- my $unapplied_sql = FS::cust_credit->unapplied_sql;
-
- my $sql = "
- SELECT SUM($unapplied_sql) FROM cust_credit
- WHERE custnum = $custnum
- ";
-
- sprintf( "%.2f", $self->scalar_sql($sql) );
-
+ my $total_credit = 0;
+ $total_credit += $_->credited foreach $self->cust_credit;
+ sprintf( "%.2f", $total_credit );
}
=item total_unapplied_credits_pkgnum PKGNUM
sub total_unapplied_payments {
my $self = shift;
-
- my $custnum = $self->custnum;
-
- my $unapplied_sql = FS::cust_pay->unapplied_sql;
-
- my $sql = "
- SELECT SUM($unapplied_sql) FROM cust_pay
- WHERE custnum = $custnum
- ";
-
- sprintf( "%.2f", $self->scalar_sql($sql) );
-
+ my $total_unapplied = 0;
+ $total_unapplied += $_->unapplied foreach $self->cust_pay;
+ sprintf( "%.2f", $total_unapplied );
}
=item total_unapplied_payments_pkgnum PKGNUM
sub total_unapplied_refunds {
my $self = shift;
- my $custnum = $self->custnum;
-
- my $unapplied_sql = FS::cust_refund->unapplied_sql;
-
- my $sql = "
- SELECT SUM($unapplied_sql) FROM cust_refund
- WHERE custnum = $custnum
- ";
-
- sprintf( "%.2f", $self->scalar_sql($sql) );
-
+ my $total_unapplied = 0;
+ $total_unapplied += $_->unapplied foreach $self->cust_refund;
+ sprintf( "%.2f", $total_unapplied );
}
=item balance
sub balance {
my $self = shift;
- $self->balance_date_range;
+ sprintf( "%.2f",
+ $self->total_owed
+ + $self->total_unapplied_refunds
+ - $self->total_unapplied_credits
+ - $self->total_unapplied_payments
+ );
}
=item balance_date TIME
sub balance_date {
my $self = shift;
- $self->balance_date_range(shift);
+ my $time = shift;
+ sprintf( "%.2f",
+ $self->total_owed_date($time)
+ + $self->total_unapplied_refunds
+ - $self->total_unapplied_credits
+ - $self->total_unapplied_payments
+ );
}
-=item balance_date_range [ START_TIME [ END_TIME [ OPTION => VALUE ... ] ] ]
+=item balance_date_range START_TIME [ END_TIME [ OPTION => VALUE ... ] ]
-Returns the balance for this customer, optionally considering invoices with
-date earlier than START_TIME, and not later than END_TIME
+Returns the balance for this customer, only considering invoices with date
+earlier than START_TIME, and optionally not later than END_TIME
(total_owed_date minus total_unapplied_credits minus total_unapplied_payments).
Times are specified as SQL fragments or numeric
=item prospect - No packages have ever been ordered
-=item ordered - Recurring packages all are new (not yet billed).
-
=item active - One or more recurring packages is active
=item inactive - No active recurring packages, but otherwise unsuspended/uncancelled (the inactive status is new - previously inactive customers were mis-identified as cancelled)
sub cust_status {
my $self = shift;
- # prospect ordered active inactive suspended cancelled
- for my $status ( FS::cust_main->statuses() ) {
+ for my $status (qw( prospect active inactive suspended cancelled )) {
my $method = $status.'_sql';
my $numnum = ( my $sql = $self->$method() ) =~ s/cust_main\.custnum/?/g;
my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
tie %statuscolor, 'Tie::IxHash',
'prospect' => '7e0079', #'000000', #black? naw, purple
'active' => '00CC00', #green
- 'ordered' => '009999', #teal? cyan?
'inactive' => '0000CC', #blue
'suspended' => 'FF9900', #yellow
'cancelled' => 'FF0000', #red
$select_count_pkgs;
}
-sub prospect_sql {
- " 0 = ( $select_count_pkgs ) ";
-}
-
-=item ordered_sql
-
-Returns an SQL expression identifying ordered cust_main records (customers with
-recurring packages not yet setup).
-
-=cut
-
-sub ordered_sql {
- " 0 < ( $select_count_pkgs AND ". FS::cust_pkg->ordered_sql. " ) ";
-}
+sub prospect_sql { "
+ 0 = ( $select_count_pkgs )
+"; }
=item active_sql
=cut
-sub active_sql {
- " 0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. " ) ";
-}
+sub active_sql { "
+ 0 < ( $select_count_pkgs AND ". FS::cust_pkg->active_sql. "
+ )
+"; }
=item inactive_sql
WHERE cust_refund.custnum = cust_main.custnum )
"; }
-=item balance_date_sql [ START_TIME [ END_TIME [ OPTION => VALUE ... ] ] ]
+=item balance_date_sql START_TIME [ END_TIME [ OPTION => VALUE ... ] ]
-Returns an SQL fragment to retreive the balance for this customer, optionally
-considering invoices with date earlier than START_TIME, and not
+Returns an SQL fragment to retreive the balance for this customer, only
+considering invoices with date earlier than START_TIME, and optionally not
later than END_TIME (total_owed_date minus total_unapplied_credits minus
total_unapplied_payments).
=cut
sub unapplied_payments_date_sql {
- my( $class, $start, $end, %opt ) = @_;
+ my( $class, $start, $end, ) = @_;
- my $cutoff = $opt{'cutoff'};
-
- my $unapp_pay = FS::cust_pay->unapplied_sql($cutoff);
+ my $unapp_pay = FS::cust_pay->unapplied_sql;
my $pay_where = $class->_money_table_where( 'cust_pay', $start, $end,
'unapplied_date'=>1 );
# parse status
##
- #prospect ordered active inactive suspended cancelled
+ #prospect active inactive suspended cancelled
if ( grep { $params->{'status'} eq $_ } FS::cust_main->statuses() ) {
my $method = $params->{'status'}. '_sql';
#push @where, $class->$method();
my $subject = delete $params->{subject};
my $html_body = delete $params->{html_body};
my $text_body = delete $params->{text_body};
- my $error = '';
- my $job = delete $params->{'job'}
- or die "email_search_result must run from the job queue.\n";
+ my $job = delete $params->{'job'};
$params->{'payby'} = [ split(/\0/, $params->{'payby'}) ]
unless ref($params->{'payby'});
my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
- my @retry_jobs = ();
- my $success = 0;
#eventually order+limit magic to reduce memory use?
foreach my $cust_main ( qsearch($sql_query) ) {
- #progressbar first, so that the count is right
- $num++;
- if ( time - $min_sec > $last ) {
- my $error = $job->update_statustext(
- int( 100 * $num / $num_cust )
- );
- die $error if $error;
- $last = time;
- }
-
my $to = $cust_main->invoicing_list_emailonly_scalar;
+ next unless $to;
- if( $to ) {
- my @message = (
+ my $error = send_email(
+ generate_email(
'from' => $from,
'to' => $to,
'subject' => $subject,
'html_body' => $html_body,
'text_body' => $text_body,
- );
-
- $error = send_email( generate_email( @message ) );
+ )
+ );
+ return $error if $error;
- if($error) {
- # queue the sending of this message so that the user can see what we
- # tried to do, and retry if desired
- my $queue = new FS::queue {
- 'job' => 'FS::Misc::process_send_email',
- 'custnum' => $cust_main->custnum,
- 'status' => 'failed',
- 'statustext' => $error,
- };
- $queue->insert(@message);
- push @retry_jobs, $queue;
- }
- else {
- $success++;
+ if ( $job ) { #progressbar foo
+ $num++;
+ if ( time - $min_sec > $last ) {
+ my $error = $job->update_statustext(
+ int( 100 * $num / $num_cust )
+ );
+ die $error if $error;
+ $last = time;
}
}
- if($success == 0 and
- (scalar(@retry_jobs) > 10 or $num == $num_cust)
- ) {
- # 10 is arbitrary, but if we have enough failures, that's
- # probably a configuration or network problem, and we
- # abort the batch and run away screaming.
- # We NEVER do this if anything was successfully sent.
- $_->delete foreach (@retry_jobs);
- return "multiple failures: '$error'\n";
- }
- }
-
- if(@retry_jobs) {
- # fail the job, but with a status message that makes it clear
- # something was sent.
- return "Sent $success, failed ".scalar(@retry_jobs).". Failed attempts placed in job queue.\n";
}
return '';
}
+use Storable qw(thaw);
+use Data::Dumper;
+use MIME::Base64;
sub process_email_search_result {
my $job = shift;
#warn "$me process_re_X $method for job $job\n" if $DEBUG;
=item notify CUSTOMER_OBJECT TEMPLATE_NAME OPTIONS
-Deprecated. Use event notification and message templates
-(L<FS::msg_template>) instead.
-
Sends a templated email notification to the customer (see L<Text::Template>).
OPTIONS is a hash and may include
}
-=item queued_bill 'custnum' => CUSTNUM [ , OPTION => VALUE ... ]
-
-Subroutine (not a method), designed to be called from the queue.
-
-Takes a list of options and values.
-
-Pulls up the customer record via the custnum option and calls bill_and_collect.
-
-=cut
-
sub queued_bill {
+ ## actual sub, not a method, designed to be called from the queue.
+ ## sets up the customer, and calls the bill_and_collect
my (%args) = @_; #, ($time, $invoice_time, $check_freq, $resetup) = @_;
-
my $cust_main = qsearchs( 'cust_main', { custnum => $args{'custnum'} } );
- warn 'bill_and_collect custnum#'. $cust_main->custnum. "\n";#log custnum w/pid
-
- $cust_main->bill_and_collect( %args );
-}
-
-sub process_bill_and_collect {
- my $job = shift;
- my $param = thaw(decode_base64(shift));
- my $cust_main = qsearchs( 'cust_main', { custnum => $param->{'custnum'} } )
- or die "custnum '$param->{custnum}' not found!\n";
- $param->{'job'} = $job;
- $param->{'fatal'} = 1; # runs from job queue, will be caught
- $param->{'retry'} = 1;
-
- $cust_main->bill_and_collect( %$param );
+ $cust_main->bill_and_collect(
+ %args,
+ );
}
sub _upgrade_data { #class method
$sth->execute or die $sth->errstr;
local($ignore_expired_card) = 1;
- local($skip_fuzzyfiles) = 1;
$class->_upgrade_otaker(%opts);
}
$error = $self->SUPER::insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
- return "error inserting cust_pay: $error";
+ return "error inserting $self: $error";
}
if ( $self->invnum ) {
$error = $cust_bill_pay->insert(%options);
if ( $error ) {
if ( $ignore_noapply ) {
- warn "warning: error inserting cust_bill_pay: $error ".
+ warn "warning: error inserting $cust_bill_pay: $error ".
"(ignore_noapply flag set; inserting cust_pay record anyway)\n";
} else {
$dbh->rollback if $oldAutoCommit;
- return "error inserting cust_bill_pay: $error";
+ return "error inserting $cust_bill_pay: $error";
}
}
}
my $conf = new FS::Conf;
- my @invoicing_list = $cust_main->invoicing_list_emailonly;
- return '' unless @invoicing_list;
+ return ''
+ unless $conf->exists('payment_receipt_email')
+ && grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list;
$cust_bill ||= ($cust_main->cust_bill)[-1]; #rather inefficient though?
if ( ( exists($opt->{'manual'}) && $opt->{'manual'} )
- || ! $conf->exists('invoice_html_statement') # XXX msg_template
+ || ! $conf->exists('invoice_html_statement')
|| ! $cust_bill
) {
- my $error = '';
+ my $receipt_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('payment_receipt_email') ],
+ ) or do {
+ warn "can't create payment receipt template: $Text::Template::ERROR";
+ return '';
+ };
+
+ my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ }
+ $cust_main->invoicing_list;
- if( $conf->exists('payment_receipt_msgnum') ) {
- my $msg_template =
- FS::msg_template->by_key($conf->config('payment_receipt_msgnum'));
- $error = $msg_template->send('cust_main'=> $cust_main, 'object'=> $self);
+ my $payby = $self->payby;
+ my $payinfo = $self->payinfo;
+ $payby =~ s/^BILL$/Check/ if $payinfo;
+ if ( $payby eq 'CARD' || $payby eq 'CHEK' ) {
+ $payinfo = $self->paymask
+ } else {
+ $payinfo = $self->decrypt($payinfo);
}
- elsif ( $conf->exists('payment_receipt_email') ) {
- my $receipt_template = new Text::Template (
- TYPE => 'ARRAY',
- SOURCE => [ map "$_\n", $conf->config('payment_receipt_email') ],
- ) or do {
- warn "can't create payment receipt template: $Text::Template::ERROR";
- return '';
- };
+ $payby =~ s/^CHEK$/Electronic check/;
+
+ my %fill_in = (
+ 'date' => time2str("%a %B %o, %Y", $self->_date),
+ 'name' => $cust_main->name,
+ 'paynum' => $self->paynum,
+ 'paid' => sprintf("%.2f", $self->paid),
+ 'payby' => ucfirst(lc($payby)),
+ 'payinfo' => $payinfo,
+ 'balance' => $cust_main->balance,
+ 'company_name' => $conf->config('company_name', $cust_main->agentnum),
+ );
- my $payby = $self->payby;
- my $payinfo = $self->payinfo;
- $payby =~ s/^BILL$/Check/ if $payinfo;
- if ( $payby eq 'CARD' || $payby eq 'CHEK' ) {
- $payinfo = $self->paymask
- } else {
- $payinfo = $self->decrypt($payinfo);
- }
- $payby =~ s/^CHEK$/Electronic check/;
-
- my %fill_in = (
- 'date' => time2str("%a %B %o, %Y", $self->_date),
- 'name' => $cust_main->name,
- 'paynum' => $self->paynum,
- 'paid' => sprintf("%.2f", $self->paid),
- 'payby' => ucfirst(lc($payby)),
- 'payinfo' => $payinfo,
- 'balance' => $cust_main->balance,
- 'company_name' => $conf->config('company_name', $cust_main->agentnum),
- );
-
- if ( $opt->{'cust_pkg'} ) {
- $fill_in{'pkg'} = $opt->{'cust_pkg'}->part_pkg->pkg;
- #setup date, other things?
- }
+ if ( $opt->{'cust_pkg'} ) {
+ $fill_in{'pkg'} = $opt->{'cust_pkg'}->part_pkg->pkg;
+ #setup date, other things?
+ }
- $error = send_email(
- 'from' => $conf->config('invoice_from', $cust_main->agentnum),
- #invoice_from??? well as good as any
- 'to' => \@invoicing_list,
- 'subject' => 'Payment receipt',
- 'body' => [ $receipt_template->fill_in( HASH => \%fill_in ) ],
- );
+ send_email(
+ 'from' => $conf->config('invoice_from', $cust_main->agentnum),
+ #invoice_from??? well as good as any
+ 'to' => \@invoicing_list,
+ 'subject' => 'Payment receipt',
+ 'body' => [ $receipt_template->fill_in( HASH => \%fill_in ) ],
+ );
- }
- else { # no payment_receipt_msgnum or payment_receipt_email
+ } else {
- my $queue = new FS::queue {
- 'paynum' => $self->paynum,
- 'job' => 'FS::cust_bill::queueable_email',
- };
+ my $queue = new FS::queue {
+ 'paynum' => $self->paynum,
+ 'job' => 'FS::cust_bill::queueable_email',
+ };
+
+ $queue->insert(
+ 'invnum' => $cust_bill->invnum,
+ 'template' => 'statement',
+ );
+
+ }
- $queue->insert(
- 'invnum' => $cust_bill->invnum,
- 'template' => 'statement',
- );
- }
-
- warn "send_receipt: $error\n" if $error;
- } #$opt{manual} || no invoice_html_statement || customer has no invoices
}
=item cust_bill_pay
=cut
sub unapplied_sql {
- my ($class, $start, $end) = @_;
+ my ($class, $start, $end) = shift;
my $bill_start = $start ? "AND cust_bill_pay._date <= $start" : '';
my $bill_end = $end ? "AND cust_bill_pay._date > $end" : '';
my $refund_start = $start ? "AND cust_pay_refund._date <= $start" : '';
warn "$me upgrading $class\n" if $DEBUG;
- ##
- # otaker/ivan upgrade
- ##
-
#not the most efficient, but hey, it only has to run once
my $where = "WHERE ( otaker IS NULL OR otaker = '' OR otaker = 'ivan' ) ".
}
- ###
- # payinfo N/A upgrade
- ###
-
- #XXX remove the 'N/A (tokenized)' part (or just this entire thing)
-
- my @na_cust_pay = qsearch( {
- 'table' => 'cust_pay',
- 'hashref' => {}, #could be encrypted# { 'payinfo' => 'N/A' },
- 'extra_sql' => "WHERE ( payinfo = 'N/A' OR paymask = 'N/AA' OR paymask = 'N/A (tokenized)' ) AND payby IN ( 'CARD', 'CHEK' )",
- } );
-
- foreach my $na ( @na_cust_pay ) {
-
- next unless $na->payinfo eq 'N/A';
-
- my $cust_pay_pending =
- qsearchs('cust_pay_pending', { 'paynum' => $na->paynum } );
- unless ( $cust_pay_pending ) {
- warn " *** WARNING: not-yet recoverable N/A card for payment ".
- $na->paynum. " (no cust_pay_pending)\n";
- next;
- }
- $na->$_($cust_pay_pending->$_) for qw( payinfo paymask );
- my $error = $na->replace;
- if ( $error ) {
- warn " *** WARNING: Error updating payinfo for payment paynum ".
- $na->paynun. ": $error\n";
- next;
- }
-
- }
-
- ###
- # otaker->usernum upgrade
- ###
-
$class->_upgrade_otaker(%opts);
}
use strict;
use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::location_Mixin
- FS::m2m_Common FS::option_Common );
+ FS::m2m_Common FS::option_Common FS::Record );
use vars qw($disable_agentcheck $DEBUG $me);
use Carp qw(cluck);
use Scalar::Util qw( blessed );
my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
- my $msgnum = $conf->config('cancel_msgnum', $self->cust_main->agentnum);
- my $error = '';
- if ( $msgnum ) {
- my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
- $error = $msg_template->send( 'cust_main' => $self->cust_main,
- 'object' => $self );
- }
- else {
- $error = send_email(
- 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
- 'to' => \@invoicing_list,
- 'subject' => ( $conf->config('cancelsubject') || 'Cancellation Notice' ),
- 'body' => [ map "$_\n", $conf->config('cancelmessage') ],
- );
- }
+ my $error = send_email(
+ 'from' => $conf->config('invoice_from', $self->cust_main->agentnum),
+ 'to' => \@invoicing_list,
+ 'subject' => ( $conf->config('cancelsubject') || 'Cancellation Notice' ),
+ 'body' => [ map "$_\n", $conf->config('cancelmessage') ],
+ );
#should this do something on errors?
}
return 'active';
}
-=item ucfirst_status
-
-Returns the status with the first character capitalized.
-
-=cut
-
-sub ucfirst_status {
- ucfirst(shift->status);
-}
-
=item statuses
Class method that returns the list of possible status strings for packages
my %labels;
#tie %labels, 'Tie::IxHash';
push @{ $labels{$_->[0]} }, $_->[1]
- foreach $self->$method(@_);
+ foreach $self->h_labels(@_);
my @labels;
foreach my $label ( keys %labels ) {
my %seen = ();
where cust_pkg.pkgpart = part_pkg.pkgpart )
"; }
-=item ordered_sql
-
-Returns an SQL expression identifying ordered packages (recurring packages not
-yet billed).
-
-=cut
-
-sub ordered_sql {
- $_[0]->recurring_sql. " AND ". $_[0]->not_yet_billed_sql;
-}
-
=item active_sql
Returns an SQL expression identifying active packages.
=cut
-sub active_sql {
- $_[0]->recurring_sql. "
+sub active_sql { "
+ ". $_[0]->recurring_sql(). "
AND cust_pkg.setup IS NOT NULL AND cust_pkg.setup != 0
AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
=cut
sub unapplied_sql {
- my ($class, $start, $end) = @_;
+ my ($class, $start, $end) = shift;
my $credit_start = $start ? "AND cust_credit_refund._date <= $start" : '';
my $credit_end = $end ? "AND cust_credit_refund._date > $end" : '';
my $pay_start = $start ? "AND cust_pay_refund._date <= $start" : '';
+++ /dev/null
-package FS::cust_tag;
-
-use strict;
-use base qw( FS::Record );
-use FS::Record qw( qsearchs );
-use FS::cust_main;
-use FS::part_tag;
-
-=head1 NAME
-
-FS::cust_tag - Object methods for cust_tag records
-
-=head1 SYNOPSIS
-
- use FS::cust_tag;
-
- $record = new FS::cust_tag \%hash;
- $record = new FS::cust_tag { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_tag object represents a customer tag. FS::cust_tag inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item custtagnum
-
-primary key
-
-=item custnum
-
-custnum
-
-=item tagnum
-
-tagnum
-
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new customer tag. To add the tag to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'cust_tag'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid customer tag. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('custtagnum')
- || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
- || $self->ut_foreign_key('tagnum', 'part_tag', 'tagnum' )
- ;
- return $error if $error;
-
- $self->SUPER::check;
-}
-
-=item cust_main
-
-=cut
-
-sub cust_main {
- my $self = shift;
- qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
-}
-
-=item part_tag
-
-=cut
-
-sub part_tag {
- my $self = shift;
- qsearchs( 'part_tag', { 'tagnum' => $self->tagnum } );
-}
-
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
+++ /dev/null
-package FS::h_inventory_item;
-
-use strict;
-use vars qw( @ISA );
-use FS::h_Common;
-use FS::inventory_item;
-
-@ISA = qw( FS::h_Common FS::inventory_item );
-
-sub table { 'h_inventory_item' };
-
-=head1 NAME
-
-FS::h_inventory_item - Historical record of inventory item activity
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-An FS::h_inventory_item object represents a change in the state of an
-inventory item.
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::inventory_item>, L<FS::h_Common>, L<FS::Record>, schema.html from the
-base documentation.
-
-=cut
-
-1;
-
+++ /dev/null
-package FS::msg_template;
-
-use strict;
-use base qw( FS::Record );
-use Text::Template;
-use FS::Misc qw( generate_email send_email );
-use FS::Conf;
-use FS::Record qw( qsearch qsearchs );
-
-use Date::Format qw( time2str );
-use HTML::Entities qw( decode_entities encode_entities ) ;
-use HTML::FormatText;
-use HTML::TreeBuilder;
-use vars '$DEBUG';
-
-$DEBUG=0;
-
-=head1 NAME
-
-FS::msg_template - Object methods for msg_template records
-
-=head1 SYNOPSIS
-
- use FS::msg_template;
-
- $record = new FS::msg_template \%hash;
- $record = new FS::msg_template { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::msg_template object represents a customer message template.
-FS::msg_template inherits from FS::Record. The following fields are currently
-supported:
-
-=over 4
-
-=item msgnum
-
-primary key
-
-=item msgname
-
-Template name.
-
-=item agentnum
-
-Agent associated with this template. Can be NULL for a global template.
-
-=item mime_type
-
-MIME type. Defaults to text/html.
-
-=item from_addr
-
-Source email address.
-
-=item subject
-
-The message subject line, in L<Text::Template> format.
-
-=item body
-
-The message body, as plain text or HTML, in L<Text::Template> format.
-
-=item disabled
-
-disabled
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new template. To add the template to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'msg_template'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid template. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('msgnum')
- || $self->ut_text('msgname')
- || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
- || $self->ut_textn('mime_type')
- || $self->ut_anything('subject')
- || $self->ut_anything('body')
- || $self->ut_enum('disabled', [ '', 'Y' ] )
- || $self->ut_textn('from_addr')
- ;
- return $error if $error;
-
- $self->mime_type('text/html') unless $self->mime_type;
-
- $self->SUPER::check;
-}
-
-=item prepare OPTION => VALUE
-
-Fills in the template and returns a hash of the 'from' address, 'to'
-addresses, subject line, and body.
-
-Options are passed as a list of name/value pairs:
-
-=over 4
-
-=item cust_main
-
-Customer object (required).
-
-=item object
-
-Additional context object (currently, can be a cust_main, cust_pkg,
-cust_bill, svc_acct, or cust_pay object).
-
-=back
-
-=cut
-
-sub prepare {
- my( $self, %opt ) = @_;
-
- my $cust_main = $opt{'cust_main'};
- my $object = $opt{'object'};
- warn "preparing template '".$self->msgname."' to cust#".$cust_main->custnum."\n"
- if($DEBUG);
-
- my $subs = $self->substitutions;
-
- ###
- # create substitution table
- ###
- my %hash;
- foreach my $obj ($cust_main, $object || ()) {
- foreach my $name (@{ $subs->{$obj->table} }) {
- if(!ref($name)) {
- # simple case
- $hash{$name} = $obj->$name();
- }
- elsif( ref($name) eq 'ARRAY' ) {
- # [ foo => sub { ... } ]
- $hash{$name->[0]} = $name->[1]->($obj);
- }
- else {
- warn "bad msg_template substitution: '$name'\n";
- #skip it?
- }
- }
- }
- $_ = encode_entities($_) foreach values(%hash);
-
-
- ###
- # clean up template
- ###
- my $subject_tmpl = new Text::Template (
- TYPE => 'STRING',
- SOURCE => $self->subject,
- );
- my $subject = $subject_tmpl->fill_in( HASH => \%hash );
-
- my $body = $self->body;
- my ($skin, $guts) = eviscerate($body);
- @$guts = map {
- $_ = decode_entities($_); # turn all punctuation back into itself
- s/\r//gs; # remove \r's
- s/<br[^>]*>/\n/gsi; # and <br /> tags
- s/<p>/\n/gsi; # and <p>
- s/<\/p>//gsi; # and </p>
- s/\240/ /gs; # and
- $_
- } @$guts;
-
- $body = '';
- while(@$skin || @$guts) {
- $body .= shift(@$skin) || '';
- $body .= shift(@$guts) || '';
- }
-
- ###
- # fill-in
- ###
-
- my $body_tmpl = new Text::Template (
- TYPE => 'STRING',
- SOURCE => $body,
- );
-
- $body = $body_tmpl->fill_in( HASH => \%hash );
-
- ###
- # and email
- ###
-
- my @to = $cust_main->invoicing_list_emailonly;
- warn "prepared msg_template with no email destination (custnum ".
- $cust_main->custnum.")\n"
- if !@to;
-
- my $conf = new FS::Conf;
-
- (
- 'from' => $self->from ||
- scalar( $conf->config('invoice_from', $cust_main->agentnum) ),
- 'to' => \@to,
- 'subject' => $subject,
- 'html_body' => $body,
- 'text_body' => HTML::FormatText->new(leftmargin => 0, rightmargin => 70
- )->format( HTML::TreeBuilder->new_from_content($body) ),
- );
-
-}
-
-=item send OPTION => VALUE
-
-Fills in the template and sends it to the customer. Options are as for
-'prepare'.
-
-=cut
-
-# broken out from prepare() in case we want to queue the sending,
-# preview it, etc.
-sub send {
- my $self = shift;
- send_email(generate_email($self->prepare(@_)));
-}
-
-# helper sub for package dates
-my $ymd = sub { $_[0] ? time2str('%Y-%m-%d', $_[0]) : '' };
-
-# needed for some things
-my $conf = new FS::Conf;
-
-#return contexts and fill-in values
-# If you add anything, be sure to add a description in
-# httemplate/edit/msg_template.html.
-sub substitutions {
- { 'cust_main' => [qw(
- display_custnum agentnum agent_name
-
- last first company
- name name_short contact contact_firstlast
- address1 address2 city county state zip
- country
- daytime night fax
-
- has_ship_address
- ship_last ship_first ship_company
- ship_name ship_name_short ship_contact ship_contact_firstlast
- ship_address1 ship_address2 ship_city ship_county ship_state ship_zip
- ship_country
- ship_daytime ship_night ship_fax
-
- paymask payname paytype payip
- num_cancelled_pkgs num_ncancelled_pkgs num_pkgs
- classname categoryname
- balance
- invoicing_list_emailonly
- cust_status ucfirst_cust_status cust_statuscolor
-
- signupdate dundate
- ),
- [ signupdate_ymd => sub { time2str('%Y-%m-%d', shift->signupdate) } ],
- [ dundate_ymd => sub { time2str('%Y-%m-%d', shift->dundate) } ],
- [ paydate_my => sub { sprintf('%02d/%04d', shift->paydate_monthyear) } ],
- [ otaker_first => sub { shift->access_user->first } ],
- [ otaker_last => sub { shift->access_user->last } ],
- [ payby => sub { FS::payby->shortname(shift->payby) } ],
- [ company_name => sub {
- $conf->config('company_name', shift->agentnum)
- } ],
- ],
- # next_bill_date
- 'cust_pkg' => [qw(
- pkgnum pkg_label pkg_label_long
- location_label
- status statuscolor
-
- start_date setup bill last_bill
- adjourn susp expire
- labels_short
- ),
- [ cancel => sub { shift->getfield('cancel') } ], # grrr...
- [ start_ymd => sub { $ymd->(shift->getfield('start_date')) } ],
- [ setup_ymd => sub { $ymd->(shift->getfield('setup')) } ],
- [ next_bill_ymd => sub { $ymd->(shift->getfield('bill')) } ],
- [ last_bill_ymd => sub { $ymd->(shift->getfield('last_bill')) } ],
- [ adjourn_ymd => sub { $ymd->(shift->getfield('adjourn')) } ],
- [ susp_ymd => sub { $ymd->(shift->getfield('susp')) } ],
- [ expire_ymd => sub { $ymd->(shift->getfield('expire')) } ],
- [ cancel_ymd => sub { $ymd->(shift->getfield('cancel')) } ],
- ],
- 'cust_bill' => [qw(
- invnum
- _date
- )],
- #XXX not really thinking about cust_bill substitutions quite yet
-
- 'svc_acct' => [qw(
- username
- ),
- [ password => sub { shift->getfield('_password') } ],
- ], # for welcome messages
- 'cust_pay' => [qw(
- paynum
- _date
- ),
- [ paid => sub { sprintf("%.2f", shift->paid) } ],
- # overrides the one in cust_main in cases where a cust_pay is passed
- [ payby => sub { FS::payby->shortname(shift->payby) } ],
- [ date => sub { time2str("%a %B %o, %Y", shift->_date) } ],
- [ payinfo => sub {
- my $cust_pay = shift;
- ($cust_pay->payby eq 'CARD' || $cust_pay->payby eq 'CHEK') ?
- $cust_pay->paymask : $cust_pay->decrypt($cust_pay->payinfo)
- } ],
- ],
- };
-}
-
-sub _upgrade_data {
- my ($self, %opts) = @_;
-
- my @fixes = (
- [ 'alerter_msgnum', 'alerter_template', '', '' ],
- [ 'cancel_msgnum', 'cancelmessage', 'cancelsubject', '' ],
- [ 'decline_msgnum', 'declinetemplate', '', '' ],
- [ 'impending_recur_msgnum', 'impending_recur_template', '', '' ],
- [ 'payment_receipt_msgnum', 'payment_receipt_email', '', '' ],
- [ 'welcome_msgnum', 'welcome_email', 'welcome_email-subject', 'welcome_email-from' ],
- [ 'warning_msgnum', 'warning_email', 'warning_email-subject', 'warning_email-from' ],
- );
-
- my $conf = new FS::Conf;
- my @agentnums = ('', map {$_->agentnum} qsearch('agent', {}));
- foreach my $agentnum (@agentnums) {
- foreach (@fixes) {
- my ($newname, $oldname, $subject, $from) = @$_;
- if ($conf->exists($oldname, $agentnum)) {
- my $new = new FS::msg_template({
- 'msgname' => $oldname,
- 'agentnum' => $agentnum,
- 'from_addr' => ($from && $conf->config($from, $agentnum)) ||
- $conf->config('invoice_from', $agentnum),
- 'subject' => ($subject && $conf->config($subject, $agentnum)) || '',
- 'mime_type' => 'text/html',
- 'body' => join('<BR>',$conf->config($oldname, $agentnum)),
- });
- my $error = $new->insert;
- die $error if $error;
- $conf->set($newname, $new->msgnum, $agentnum);
- $conf->delete($oldname, $agentnum);
- $conf->delete($from, $agentnum) if $from;
- $conf->delete($subject, $agentnum) if $subject;
- }
- }
- }
-}
-
-sub eviscerate {
- # Every bit as pleasant as it sounds.
- #
- # We do this because Text::Template::Preprocess doesn't
- # actually work. It runs the entire template through
- # the preprocessor, instead of the code segments. Which
- # is a shame, because Text::Template already contains
- # the code to do this operation.
- my $body = shift;
- my (@outside, @inside);
- my $depth = 0;
- my $chunk = '';
- while($body || $chunk) {
- my ($first, $delim, $rest);
- # put all leading non-delimiters into $first
- ($first, $rest) =
- ($body =~ /^((?:\\[{}]|[^{}])*)(.*)$/s);
- $chunk .= $first;
- # put a leading delimiter into $delim if there is one
- ($delim, $rest) =
- ($rest =~ /^([{}]?)(.*)$/s);
-
- if( $delim eq '{' ) {
- $chunk .= '{';
- if( $depth == 0 ) {
- push @outside, $chunk;
- $chunk = '';
- }
- $depth++;
- }
- elsif( $delim eq '}' ) {
- $depth--;
- if( $depth == 0 ) {
- push @inside, $chunk;
- $chunk = '';
- }
- $chunk .= '}';
- }
- else {
- # no more delimiters
- if( $depth == 0 ) {
- push @outside, $chunk . $rest;
- } # else ? something wrong
- last;
- }
- $body = $rest;
- }
- (\@outside, \@inside);
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
my $self = shift;
if ( scalar(@_) ) { #set
my $otaker = shift;
- my $access_user = qsearchs('access_user', { 'username' => $otaker } );
- if ( !$access_user && $otaker =~ /^(.+), (.+)$/ ) { #same as below..
- my($lastname, $firstname) = ($1, $2);
- $otaker = lc($firstname.$lastname);
- $access_user = qsearchs('access_user', { 'first' => $firstname,
- 'last' => $lastname } )
- || qsearchs('access_user', { 'username' => $otaker } );
- }
- croak "can't set otaker: $otaker not found!" unless $access_user; #confess?
+ my $access_user = qsearchs('access_user', { 'username' => $otaker } )
+ or croak "can't set otaker: $otaker not found!"; #confess?
$self->usernum( $access_user->usernum );
$otaker; #not sure return is used anywhere, but just in case
} else { #get
my $class = shift;
my $table = $class->table;
- my $limit = ( $table eq 'cust_attachment' ? 10 : 1000 );
-
while ( 1 ) {
my @records = qsearch({
'table' => $table,
'hashref' => {},
- 'extra_sql' => "WHERE otaker IS NOT NULL LIMIT $limit",
+ 'extra_sql' => 'WHERE otaker IS NOT NULL LIMIT 1000',
});
last unless @records;
foreach my $record (@records) {
eval { $record->otaker($record->otaker) };
if ( $@ ) {
- my $username = $record->otaker;
- my($lastname, $firstname) = ( 'User', 'Legacy' );
- if ( $username =~ /^(.+), (.+)$/ ) {
- ($lastname, $firstname) = ($1, $2);
- $username = lc($firstname.$lastname);
- }
my $access_user = new FS::access_user {
- 'username' => $username,
+ 'username' => $record->otaker,
'_password' => 'CHANGEME',
- 'first' => $firstname,
- 'last' => $lastname,
+ 'first' => 'Legacy',
+ 'last' => 'User',
'disabled' => 'Y',
};
my $error = $access_user->insert;
+++ /dev/null
-package FS::part_event::Action::notice;
-
-use strict;
-use base qw( FS::part_event::Action );
-use FS::Record qw( qsearchs );
-use FS::msg_template;
-
-sub description { 'Send a notice from a message template'; }
-
-#sub eventtable_hashref {
-# { 'cust_main' => 1,
-# 'cust_bill' => 1,
-# 'cust_pkg' => 1,
-# };
-#}
-
-sub option_fields {
- (
- 'msgnum' => { 'label' => 'Template',
- 'type' => 'select-table',
- 'table' => 'msg_template',
- 'name_col' => 'msgname',
- 'disable_empty' => 1,
- },
- );
-}
-
-sub default_weight { 55; } #?
-
-sub do_action {
- my( $self, $object ) = @_;
-
- my $cust_main = $self->cust_main($object);
-
- my $msgnum = $self->option('msgnum');
-
- my $msg_template = qsearchs('msg_template', { 'msgnum' => $msgnum } )
- or die "Template $msgnum not found";
-
- $msg_template->send(
- 'cust_main' => $cust_main,
- 'object' => $object,
- );
-
-}
-
-1;
+++ /dev/null
-package FS::part_event::Condition::pkg_freq;
-
-use strict;
-use FS::Misc;
-use FS::cust_pkg;
-
-use base qw( FS::part_event::Condition );
-
-sub description { 'Package billing frequency'; }
-
-sub option_fields {
- my $freqs = FS::Misc::pkg_freqs();
- (
- 'freq' => { 'label' => 'Frequency',
- 'type' => 'select',
- 'labels' => $freqs,
- 'options' => [ keys(%$freqs) ],
- },
- );
-}
-
-sub eventtable_hashref {
- { 'cust_main' => 0,
- 'cust_bill' => 0,
- 'cust_pkg' => 1,
- };
-}
-
-sub condition {
- my($self, $cust_pkg) = @_;
-
- $cust_pkg->part_pkg->freq eq $self->option('freq')
-}
-
-1;
-
sub _export_replace_svc_domain {
my( $self, $new, $old ) = (shift, shift, shift);
- #let's just do the rename part realtime rather than trying to queue
- #w/dependencies. we don't want FS winding up out-of-sync with the wrong
- #username and a queued job anyway. right??
if ( $old->domain ne $new->domain ) {
- eval { $self->communigate_pro_runcommand(
- 'RenameDomain', $old->domain, $new->domain,
- ) };
- return $@ if $@;
+ my $error = $self->communigate_pro_queue( $new->svcnum, 'RenameDomain',
+ $old->domain, $new->domain,
+ );
+ return $error if $error;
}
-
my %settings = ();
$settings{'AccountsLimit'} = $new->max_accounts
if $old->max_accounts ne $new->max_accounts;
}
-sub export_getsettings_svc_forward {
- my($self, $svc_forward, $settingsref, $defaultref ) = @_;
-
- my $dest = eval { $self->communigate_pro_runcommand(
- 'GetForwarder',
- ($svc_forward->src || $svc_forward->srcsvc_acct->email),
- ) };
- return $@ if $@;
-
- my $settings = { 'Destination' => $dest };
-
- %{$settingsref} = %$settings;
- %{$defaultref} = ();
-
- '';
-}
-
sub _rule2string {
my $rule = shift;
my($priority, $name, $conditions, $actions, $comment) = @$rule;
+++ /dev/null
-package FS::part_export::cust_http;
-
-use vars qw( @ISA %info );
-use FS::part_export::http;
-use Tie::IxHash;
-
-@ISA = qw( FS::part_export::http );
-
-tie my %options, 'Tie::IxHash', %FS::part_export::http::options;
-
-$options{'insert_data'}->{'default'} = join("\n",
- "action 'insert'",
- "custnum \$cust_main->custnum",
- "first \$cust_main->first",
- "last \$cust_main->get('last')",
- ( map "$_ \$cust_main->$_", qw( company address1 address2 city county state zip country daytime night fax last ) ),
- "email \$cust_main->invoicing_list_emailonly_scalar",
-);
-$options{'delete_data'}->{'default'} = join("\n",
- "action 'delete'",
- "custnum \$cust_main->custnum",
-);
-$options{'replace_data'}->{'default'} = join("\n",
- "action 'replace'",
- "custnum \$new_cust_main->custnum",
- "first \$new_cust_main->first",
- "last \$new_cust_main->get('last')",
- ( map "$_ \$cust_main->$_", qw( company address1 address2 city county state zip country daytime night fax last ) ),
- "email \$new_cust_main->invoicing_list_emailonly_scalar",
-);
-
-%info = (
- 'svc' => 'cust_main',
- 'desc' => 'Send an HTTP or HTTPS GET or POST request, for customers.',
- 'options' => \%options,
- 'notes' => <<'END'
-Send an HTTP or HTTPS GET or POST to the specified URL on customer addition,
-modification and deletion. For HTTPS support,
-<a href="http://search.cpan.org/dist/Crypt-SSLeay">Crypt::SSLeay</a>
-or <a href="http://search.cpan.org/dist/IO-Socket-SSL">IO::Socket::SSL</a>
-is required.
-END
-);
-
-1;
'job' => 'FS::part_export::domreg_opensrs::renew_through',
};
$queue->insert( $self, $svc_domain ); #_export_insert with 'R' action?
+
+ return '';
}
## Domain registration exports do nothing on replace. Mainly because we haven't decided what they should do.
Attempts to renew the domain through the specified date. If no date is
provided it is gleaned from the associated cust_pkg bill date
-Like some export functions, dies on failure or returns undef on success.
-It is always called from the queue.
+Like most export functions, returns an error message on failure or undef on success.
=cut
warn "$me: renew_through called\n" if $DEBUG;
eval "use Net::OpenSRS;";
- die $@ if $@;
+ return $@ if $@;
unless ( $date ) {
my $cust_pkg = $svc_domain->cust_svc->cust_pkg;
- die "Can't renew: no date specified and domain is not in a package."
+ return "Can't renew: no date specified and domain is not in a package."
unless $cust_pkg;
$date = $cust_pkg->bill;
}
my $err = $self->is_supported_domain( $svc_domain );
- die $err if $err;
+ return $err if $err;
warn "$me: checking status\n" if $DEBUG;
my $rv = $self->get_status($svc_domain);
- die "Domain ". $svc_domain->domain. " is not renewable"
+ return "Domain ". $svc_domain->domain. " is not renewable"
unless $rv->{expdate};
- die "Can't parse expiration date for ". $svc_domain->domain
+ return "Can't parse expiration date for ". $svc_domain->domain
unless $rv->{expdate} =~ /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
my ($year,$month,$day,$hour,$minute,$second) = ($1,$2,$3,$4,$5,$6);
$years++;
$exp->add( 'years' => 1 );
- die "Can't renew ". $svc_domain->domain. " for more than 10 years."
+ return "Can't renew ". $svc_domain->domain. " for more than 10 years."
if $years > 10; #no infinite loop
}
- return '' unless $years;
-
- warn "$me: renewing ". $svc_domain->domain. " for $years years\n" if $DEBUG;
+ warn "$me: renewing ". $svc_domain->domain. "for $years years\n" if $DEBUG;
my $srs = $self->get_srs;
$rv = $srs->make_request(
{
}
}
);
- die $rv->{response_text} unless $rv->{is_success};
+ return $rv->{response_text} unless $rv->{is_success};
return ''; # Should only get here if renewal succeeded
}
package FS::part_export::http;
-use base qw( FS::part_export );
-use vars qw( %options %info );
+use vars qw(@ISA %info);
use Tie::IxHash;
+use FS::part_export;
-tie %options, 'Tie::IxHash',
+@ISA = qw(FS::part_export);
+
+tie my %options, 'Tie::IxHash',
'method' => { label =>'Method',
type =>'select',
#options =>[qw(POST GET)],
return unless $self->option("${action}_data");
- my $cust_main = $svc_x->table eq 'cust_main'
- ? $svc_x
- : $svc_x->cust_svc->cust_pkg->cust_main;
-
$self->http_queue( $svc_x->svcnum,
$self->option('method'),
$self->option('url'),
return unless $self->option('replace_data');
- my $new_cust_main = $new->table eq 'cust_main'
- ? $new
- : $new->cust_svc->cust_pkg->cust_main;
- my $cust_main = $new_cust_main; #so folks can use $new_cust_main or $cust_main
-
- $self->http_queue( $new->svcnum,
+ $self->http_queue( $svc_x->svcnum,
$self->option('method'),
$self->option('url'),
map {
/^\s*(\S+)\s+(.*)$/ or /()()/;
my( $field, $value_expression ) = ( $1, $2 );
- my $value = eval $value_expression;
die $@ if $@;
( $field, $value );
} split(/\n/, $self->option('replace_data') )
sub http_queue {
my($self, $svcnum) = (shift, shift);
- my $queue = new FS::queue { 'job' => "FS::part_export::http::http" };
- $queue->svcnum($svcnum) if $svcnum;
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::http::http",
+ };
$queue->insert( @_ );
}
'dn' => { label=>'Root DN' },
'password' => { label=>'Root DN password' },
'userdn' => { label=>'User DN' },
- 'key_attrib' => { label=>'Key attribute name',
- default=>'uid' },
'attributes' => { label=>'Attributes',
type=>'textarea',
default=>join("\n",
sub rebless { shift; }
-sub svc_context_eval {
- # This should possibly be in svc_Common?
- # Except the only places we use it are here and in shellcommands,
- # and it's not even the same version.
- my $svc_acct = shift;
- no strict 'refs';
- ${$_} = $svc_acct->getfield($_) foreach $svc_acct->fields;
- ${$_} = $svc_acct->$_() foreach qw( domain ldap_password );
- my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
- if ( $cust_pkg ) {
- my $cust_main = $cust_pkg->cust_main;
- ${$_} = $cust_main->getfield($_) foreach qw(first last);
+sub _export_insert {
+ my($self, $svc_acct) = (shift, shift);
+
+ #false laziness w/shellcommands.pm
+ {
+ no strict 'refs';
+ ${$_} = $svc_acct->getfield($_) foreach $svc_acct->fields;
+ ${$_} = $svc_acct->$_() foreach qw( domain );
+ my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
+ if ( $cust_pkg ) {
+ my $cust_main = $cust_pkg->cust_main;
+ ${$_} = $cust_main->getfield($_) foreach qw(first last);
+ }
}
- # DEPRECATED, probably fails for non-plain password encoding
$crypt_password = ''; #surpress "used only once" warnings
$crypt_password = '{crypt}'. crypt( $svc_acct->_password,
$saltset[int(rand(64))].$saltset[int(rand(64))] );
- return map { eval(qq("$_")) } @_ ;
-}
-
-sub key_attrib {
- my $self = shift;
- return $self->option('key_attrib') if $self->option('key_attrib');
- # otherwise, guess that it's the one that's set to $username
- foreach ( split("\n",$self->option('attributes')) ) {
- /^\s*(\w+)\s+\$username\s*$/ && return $1;
- }
- # can't recover from that, but we can fail in a more obvious way
- # than the old code did...
- die "no key_attrib set in LDAP export\n";
-}
-
-sub ldap_attrib {
- # Convert the svc_acct to its LDAP attribute set.
- my($self, $svc_acct) = (shift, shift);
+ my $username_attrib;
my %attrib = map { /^\s*(\w+)\s+(.*\S)\s*$/;
- ( $1 => $2 ); }
+ $username_attrib = $1 if $2 eq '$username';
+ ( $1 => eval(qq("$2")) ); }
grep { /^\s*(\w+)\s+(.*\S)\s*$/ }
split("\n", $self->option('attributes'));
- my @vals = svc_context_eval($svc_acct, values(%attrib));
- @attrib{keys(%attrib)} = @vals;
-
if ( $self->option('radius') ) {
foreach my $table (qw(reply check)) {
my $method = "radius_$table";
}
}
}
- return %attrib;
-}
-
-sub _export_insert {
- my($self, $svc_acct) = (shift, shift);
- my $err_or_queue = $self->ldap_queue(
- $svc_acct->svcnum,
- 'insert',
- $self->key_attrib,
- $self->ldap_attrib($svc_acct),
- );
+ my $err_or_queue = $self->ldap_queue( $svc_acct->svcnum, 'insert',
+ #$svc_acct->username,
+ $username_attrib,
+ %attrib );
return $err_or_queue unless ref($err_or_queue);
+ #groups with LDAP?
+ #my @groups = $svc_acct->radius_groups;
+ #if ( @groups ) {
+ # my $err_or_queue = $self->ldap_queue(
+ # $svc_acct->svcnum, 'usergroup_insert',
+ # $svc_acct->username, @groups );
+ # return $err_or_queue unless ref($err_or_queue);
+ #}
+
'';
}
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
+ return "can't (yet?) change username with ldap"
+ if $old->username ne $new->username;
+
+ return "ldap replace unimplemented";
+
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
my $jobnum = '';
+ #if ( $old->username ne $new->username ) {
+ # my $err_or_queue = $self->ldap_queue( $new->svcnum, 'rename',
+ # $new->username, $old->username );
+ # unless ( ref($err_or_queue) ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $err_or_queue;
+ # }
+ # $jobnum = $err_or_queue->jobnum;
+ #}
+
+ foreach my $table (qw(reply check)) {
+ my $method = "radius_$table";
+ my %new = $new->$method();
+ my %old = $old->$method();
+ if ( grep { !exists $old{$_} #new attributes
+ || $new{$_} ne $old{$_} #changed
+ } keys %new
+ ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'insert',
+ $table, $new->username, %new );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
- # the Lazy way: nuke the entry and recreate it.
- # any reason this shouldn't work? Freeside _has_ to have
- # write access to these entries and their parent DN.
- my $key = $self->key_attrib;
- my %attrib = $self->ldap_attrib($old);
- my $err_or_queue = $self->ldap_queue(
- $old->svcnum,
- 'delete',
- $key,
- $attrib{$key}
- );
- if( !ref($err_or_queue) ) {
- $dbh->rollback if $oldAutoCommit;
- return $err_or_queue;
+ my @del = grep { !exists $new{$_} } keys %old;
+ if ( @del ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'attrib_delete',
+ $table, $new->username, @del );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
}
- $jobnum = $err_or_queue->jobnum;
- $err_or_queue = $self->ldap_queue(
- $new->svcnum,
- 'insert',
- $key,
- $self->ldap_attrib($new)
- );
- if( !ref($err_or_queue) ) {
- $dbh->rollback if $oldAutoCommit;
- return $err_or_queue;
+
+ # (sorta) false laziness with FS::svc_acct::replace
+ my @oldgroups = @{$old->usergroup}; #uuuh
+ my @newgroups = $new->radius_groups;
+ my @delgroups = ();
+ foreach my $oldgroup ( @oldgroups ) {
+ if ( grep { $oldgroup eq $_ } @newgroups ) {
+ @newgroups = grep { $oldgroup ne $_ } @newgroups;
+ next;
+ }
+ push @delgroups, $oldgroup;
}
- $err_or_queue = $err_or_queue->depend_insert($jobnum);
- if( $err_or_queue ) {
- $dbh->rollback if $oldAutoCommit;
- return $err_or_queue;
+
+ if ( @delgroups ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'usergroup_delete',
+ $new->username, @delgroups );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ if ( @newgroups ) {
+ my $err_or_queue = $self->ldap_queue( $new->svcnum, 'usergroup_insert',
+ $new->username, @newgroups );
+ unless ( ref($err_or_queue) ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $err_or_queue;
+ }
+ if ( $jobnum ) {
+ my $error = $err_or_queue->depend_insert( $jobnum );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
sub _export_delete {
my( $self, $svc_acct ) = (shift, shift);
-
- my $key = $self->key_attrib;
- my ( $val ) = map { /^\s*$key\s+(.*\S)\s*$/ ? $1 : () }
- split("\n", $self->option('attributes'));
- ( $val ) = svc_context_eval($svc_acct, $val);
+ return "ldap delete unimplemented";
my $err_or_queue = $self->ldap_queue( $svc_acct->svcnum, 'delete',
- $key, $val );
+ $svc_acct->username );
ref($err_or_queue) ? '' : $err_or_queue;
}
sub ldap_insert { #subroutine, not method
my $ldap = ldap_connect(shift, shift, shift);
- my( $userdn, $key_attrib, %attrib ) = @_;
+ my( $userdn, $username_attrib, %attrib ) = @_;
- $userdn = "$key_attrib=$attrib{$key_attrib}, $userdn";
+ $userdn = "$username_attrib=$attrib{$username_attrib}, $userdn"
+ if $username_attrib;
#icky hack, but should be unsurprising to the LDAPers
foreach my $key ( grep { $attrib{$_} =~ /,/ } keys %attrib ) {
$attrib{$key} = [ split(/,/, $attrib{$key}) ];
$ldap->unbind;
}
-sub ldap_delete {
- my $ldap = ldap_connect(shift, shift, shift);
-
- my $entry = ldap_fetch($ldap, @_);
- if($entry) {
- my $status = $ldap->delete($entry);
- die 'LDAP error: '.$status->error."\n" if $status->is_error;
- }
- $ldap->unbind;
- # should failing to find the entry be fatal?
- # if it is, it will block unprovisioning the service, which is a pain.
-}
-
-sub ldap_fetch {
- # avoid needless duplication in delete and modify
- my( $ldap, $userdn, %key_data ) = @_;
- my $filter = join('', map { "($_=$key_data{$_})" } keys(%key_data));
-
- my $status = $ldap->search( base => $userdn,
- scope => 'one',
- filter => $filter );
- die 'LDAP error: '.$status->error."\n" if $status->is_error;
- my ($entry) = $status->entries;
- warn "Entry '$filter' not found in LDAP\n" if !$entry;
- return $entry;
-}
+#sub ldap_delete { #subroutine, not method
+# my $dbh = ldap_connect(shift, shift, shift);
+# my $username = shift;
+#
+# foreach my $table (qw( radcheck radreply usergroup )) {
+# my $sth = $dbh->prepare( "DELETE FROM $table WHERE UserName = ?" );
+# $sth->execute($username)
+# or die "can't delete from $table table: ". $sth->errstr;
+# }
+# $dbh->disconnect;
+#}
sub ldap_connect {
my( $machine, $dn, $password ) = @_;
sub can_discount { 0; }
sub freqs_href {
- # moved to FS::Misc to make this accessible to other packages
- # at initialization
- FS::Misc::pkg_freqs();
+ #method, class method or sub? #my $self = shift;
+
+ tie my %freq, 'Tie::IxHash',
+ '0' => '(no recurring fee)',
+ '1h' => 'hourly',
+ '1d' => 'daily',
+ '2d' => 'every two days',
+ '3d' => 'every three days',
+ '1w' => 'weekly',
+ '2w' => 'biweekly (every 2 weeks)',
+ '1' => 'monthly',
+ '45d' => 'every 45 days',
+ '2' => 'bimonthly (every 2 months)',
+ '3' => 'quarterly (every 3 months)',
+ '4' => 'every 4 months',
+ '137d' => 'every 4 1/2 months (137 days)',
+ '6' => 'semiannually (every 6 months)',
+ '12' => 'annually',
+ '13' => 'every 13 months (annually +1 month)',
+ '24' => 'biannually (every 2 years)',
+ '36' => 'triannually (every 3 years)',
+ '48' => '(every 4 years)',
+ '60' => '(every 5 years)',
+ '120' => '(every 10 years)',
+ ;
+
+ \%freq;
+
}
=item freq_pretty
use FS::rate_detail;
use FS::part_pkg::recur_Common;
-use List::Util qw(first min);
-
@ISA = qw(FS::part_pkg::recur_Common);
-$DEBUG = 1;
-
-tie my %cdr_svc_method, 'Tie::IxHash',
- 'svc_phone.phonenum' => 'Phone numbers (svc_phone.phonenum)',
- 'svc_pbx.title' => 'PBX name (svc_pbx.title)',
-;
+$DEBUG = 0;
tie my %rating_method, 'Tie::IxHash',
'prefix' => 'Rate calls by using destination prefix to look up a region and rate according to the internal prefix and rate tables',
'select_options' => \%FS::part_pkg::recur_Common::recur_method,
},
- 'cdr_svc_method' => { 'name' => 'CDR service matching method',
- 'type' => 'radio',
- 'options' => \%cdr_svc_method,
- },
-
'rating_method' => { 'name' => 'Rating method',
'type' => 'radio',
'options' => \%rating_method,
'use_cdrtypenum' => { 'name' => 'Do not charge for CDRs where the CDR Type is not set to: ',
},
- 'skip_dst_prefix' => { 'name' => 'Do not charge for CDRs where the destination number starts with any of these values:',
- },
-
'skip_dcontext' => { 'name' => 'Do not charge for CDRs where the dcontext is set to any of these (comma-separated) values:',
},
'skip_dstchannel_prefix' => { 'name' => 'Do not charge for CDRs where the dstchannel starts with:',
},
- 'skip_src_length_more' => { 'name' => 'Do not charge for CDRs where the source is more than this many digits:',
- },
-
- 'noskip_src_length_accountcode_tollfree' => { 'name' => 'Do charge for CDRs where source is equal or greater than the specified digits and accountcode is toll free',
- 'type' => 'checkbox',
- },
-
- 'accountcode_tollfree_ratenum' => {
- 'name' => 'Optional alternate rate plan when accountcode is toll free',
- 'type' => 'select',
- 'select_table' => 'rate',
- 'select_key' => 'ratenum',
- 'select_label' => 'ratename',
- 'disable_empty' => 0,
- 'empty_label' => '',
- },
-
'skip_dst_length_less' => { 'name' => 'Do not charge for CDRs where the destination is less than this many digits:',
},
'fieldorder' => [qw(
setup_fee recur_fee recur_temporality unused_credit
recur_method cutoff_day
- cdr_svc_method
rating_method ratenum min_charge sec_granularity
ignore_unrateable
default_prefix
disable_tollfree
use_amaflags use_disposition
use_disposition_taqua use_carrierid use_cdrtypenum
- skip_dcontext skip_dst_prefix
- skip_dstchannel_prefix skip_src_length_more
- noskip_src_length_accountcode_tollfree
- accountcode_tollfree_ratenum
+ skip_dcontext skip_dstchannel_prefix
skip_dst_length_less skip_lastapp
use_duration
411_rewrite
# my $downstream_cdr = '';
- my $cdr_svc_method = $self->option('cdr_svc_method')||'svc_phone.phonenum';
my $rating_method = $self->option('rating_method') || 'prefix';
my $intl = $self->option('international_prefix') || '011';
my $domestic_prefix = $self->option('domestic_prefix');
@dirass = split(',', $dirass);
}
- my %interval_cache = (); # for timed rates
-
#for check_chargable, so we don't keep looking up options inside the loop
my %opt_cache = ();
die $@ if $@;
my $csv = new Text::CSV_XS;
- my($svc_table, $svc_field) = split('\.', $cdr_svc_method);
-
foreach my $cust_svc (
- grep { $_->part_svc->svcdb eq $svc_table } $cust_pkg->cust_svc
+ grep { $_->part_svc->svcdb eq 'svc_phone' } $cust_pkg->cust_svc
) {
- my $svc_x = $cust_svc->svc_x;
+ my $svc_phone = $cust_svc->svc_x;
foreach my $cdr (
- $svc_x->get_cdrs(
+ $svc_phone->get_cdrs(
'disable_src' => $self->option('disable_src'),
'default_prefix' => $self->option('default_prefix'),
'status' => '',
my $rate_detail;
my( $rate_region, $regionnum );
- my $rate;
my $pretty_destnum;
my $charge = '';
my $seconds = '';
- my $weektime = '';
my $regionname = '';
my $classnum = '';
- my $countrycode;
- my $number;
-
my @call_details = ();
if ( $rating_method eq 'prefix' ) {
# (or calling station id for toll free calls)
###
- my( $to_or_from );
+ my( $to_or_from, $number );
if ( $cdr->is_tollfree && ! $disable_tollfree )
{ #tollfree call
$to_or_from = 'from';
# $dest =~ s/\@(.*)$// and $siphost = $1; # @10.54.32.1, @sip.example.com
#determine the country code
- $countrycode = '';
+ my $countrycode;
if ( $number =~ /^$intl(((\d)(\d))(\d))(\d+)$/
|| $number =~ /^\+(((\d)(\d))(\d))(\d+)$/
)
#asterisks here causes inserting the detail to barf, so:
$pretty_destnum =~ s/\*//g;
- my $eff_ratenum = $cdr->is_tollfree('accountcode')
- ? $cust_pkg->part_pkg->option('accountcode_tollfree_ratenum')
- : '';
- $eff_ratenum ||= $ratenum;
- $rate = qsearchs('rate', { 'ratenum' => $eff_ratenum })
- or die "ratenum $eff_ratenum not found!";
-
- my @ltime = localtime($cdr->startdate);
- $weektime = $ltime[0] +
- $ltime[1]*60 + #minutes
- $ltime[2]*3600 + #hours
- $ltime[6]*86400; #days since sunday
- # if there's no timed rate_detail for this time/region combination,
- # dest_detail returns the default. There may still be a timed rate
- # that applies after the starttime of the call, so be careful...
+ my $rate = qsearchs('rate', { 'ratenum' => $ratenum })
+ or die "ratenum $ratenum not found!";
+
$rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
'phonenum' => $number,
- 'weektime' => $weektime,
});
if ( $rate_detail ) {
"and rate detail $rate_detail\n"
if $DEBUG;
- if ( !exists($interval_cache{$regionnum}) ) {
- my @intervals = (
- sort { $a->stime <=> $b->stime }
- map { my $r = $_->rate_time; $r ? $r->intervals : () }
- $rate->rate_detail
- );
- $interval_cache{$regionnum} = \@intervals;
- warn " cached ".scalar(@intervals)." interval(s)\n"
- if $DEBUG;
- }
-
} elsif ( $ignore_unrateable ) {
$rate_region = '';
# } else { #pass upstream price through
#
# $charge = sprintf('%.2f', $cdr->upstream_price);
-# warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
# $charges += $charge;
#
# @call_details = (
#XXX $charge = sprintf('%.2f', $cdr->upstream_price);
$charge = sprintf('%.3f', $cdr->upstream_price);
$charges += $charge;
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
@call_details = ($cdr->downstream_csv( 'format' => $output_format,
'charge' => $charge,
$charge = sprintf('%.4f', ( $self->option('min_charge') * $minutes )
+ 0.0000000001 ); #so 1.00005 rounds to 1.0001
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
$charges += $charge;
@call_details = ($cdr->downstream_csv( 'format' => $output_format,
unless ( @call_details || ( $charge ne '' && $charge == 0 ) ) {
- my $seconds_left = $use_duration ? $cdr->duration : $cdr->billsec;
- # charge for the first (conn_sec) seconds
- $seconds = min($seconds_left, $rate_detail->conn_sec);
- $seconds_left -= $seconds;
- $weektime += $seconds;
- $charge = sprintf("%.02f", $rate_detail->conn_charge);
-
- my $total_minutes = 0;
- my $whole_minutes = 1;
- my $etime;
- while($seconds_left) {
- my $ratetimenum = $rate_detail->ratetimenum; # may be empty
-
- # find the end of the current rate interval
- if(@{ $interval_cache{$regionnum} } == 0) {
- # There are no timed rates in this group, so just stay
- # in the default rate_detail for the entire duration.
- # Set an "end" of 1 past the end of the current call.
- $etime = $weektime + $seconds_left + 1;
- }
- elsif($ratetimenum) {
- # This is a timed rate, so go to the etime of this interval.
- # If it's followed by another timed rate, the stime of that
- # interval should match the etime of this one.
- my $interval = $rate_detail->rate_time->contains($weektime);
- $etime = $interval->etime;
- }
- else {
- # This is a default rate, so use the stime of the next
- # interval in the sequence.
- my $next_int = first { $_->stime > $weektime }
- @{ $interval_cache{$regionnum} };
- if ($next_int) {
- $etime = $next_int->stime;
- }
- else {
- # weektime is near the end of the week, so decrement
- # it by a full week and use the stime of the first
- # interval.
- $weektime -= (3600*24*7);
- $etime = $interval_cache{$regionnum}->[0]->stime;
- }
- }
-
- my $charge_sec = min($seconds_left, $etime - $weektime);
+ $included_min{$regionnum} = $rate_detail->min_included
+ unless exists $included_min{$regionnum};
- $seconds_left -= $charge_sec;
+ my $granularity = $rate_detail->sec_granularity;
- $included_min{$regionnum}{$ratetimenum} = $rate_detail->min_included
- unless exists $included_min{$regionnum}{$ratetimenum};
+ # length($cdr->billsec) ? $cdr->billsec : $cdr->duration;
+ $seconds = $use_duration ? $cdr->duration : $cdr->billsec;
- my $granularity = $rate_detail->sec_granularity;
- $whole_minutes = 0 if $granularity;
+ $seconds -= $rate_detail->conn_sec;
+ $seconds = 0 if $seconds < 0;
- # should this be done in every rate interval?
- $charge_sec += $granularity - ( $charge_sec % $granularity )
- if $charge_sec # don't granular-ize 0 billsec calls (bills them)
- && $granularity; # 0 is per call
- my $minutes = sprintf("%.1f", $charge_sec / 60);
- $minutes =~ s/\.0$// if $granularity == 60;
+ $seconds += $granularity - ( $seconds % $granularity )
+ if $seconds # don't granular-ize 0 billsec calls (bills them)
+ && $granularity; # 0 is per call
+ my $minutes = sprintf("%.1f", $seconds / 60);
+ $minutes =~ s/\.0$// if $granularity == 60;
- $seconds += $charge_sec;
+ # per call rather than per minute
+ $minutes = 1 unless $granularity;
- # per call rather than per minute
- $minutes = 1 unless $granularity;
- $seconds_left = 0 unless $granularity;
+ $included_min{$regionnum} -= $minutes;
- $included_min{$regionnum}{$ratetimenum} -= $minutes;
-
- if ( $included_min{$regionnum}{$ratetimenum} <= 0 ) {
- my $charge_min = 0 - $included_min{$regionnum}{$ratetimenum}; #XXX should preserve
- #(display?) this
- $included_min{$regionnum}{$ratetimenum} = 0;
- $charge += sprintf('%.2f', ($rate_detail->min_charge * $charge_min)
- + 0.00000001 ); #so 1.005 rounds to 1.01
- }
+ $charge = sprintf('%.2f', $rate_detail->conn_charge);
- # choose next rate_detail
- $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
- 'phonenum' => $number,
- 'weektime' => $etime })
- if($seconds_left);
- # we have now moved forward to $etime
- $weektime = $etime;
+ if ( $included_min{$regionnum} < 0 ) {
+ my $charge_min = 0 - $included_min{$regionnum}; #XXX should preserve
+ #(display?) this
+ $included_min{$regionnum} = 0;
+ $charge += sprintf('%.2f', ($rate_detail->min_charge * $charge_min)
+ + 0.00000001 ); #so 1.005 rounds to 1.01
+ $charge = sprintf('%.2f', $charge);
+ $charges += $charge;
+ }
- } #while $seconds_left
# this is why we need regionnum/rate_region....
warn " (rate region $rate_region)\n" if $DEBUG;
- $total_minutes = sprintf("%.1f", $seconds / 60);
- $total_minutes =~ s/\.0$// if $whole_minutes;
+ @call_details = (
+ $cdr->downstream_csv( 'format' => $output_format,
+ 'granularity' => $granularity,
+ 'minutes' => $minutes,
+ 'charge' => $charge,
+ 'pretty_dst' => $pretty_destnum,
+ 'dst_regionname' => $regionname,
+ )
+ );
$classnum = $rate_detail->classnum;
- $charge = sprintf('%.2f', $charge);
- @call_details = (
- $cdr->downstream_csv( 'format' => $output_format,
- 'granularity' => $rate_detail->sec_granularity,
- 'minutes' => $total_minutes,
- # why do we go through this hocus-pocus?
- # the cdr *will* show duration here
- # if we forego the 'minutes' key
- # duration vs billsec?
- 'charge' => $charge,
- 'pretty_dst' => $pretty_destnum,
- 'dst_regionname' => $regionname,
- )
- );
- } #if(there is a rate_detail)
-
+ }
if ( $charge > 0 ) {
#just use FS::cust_bill_pkg_detail objects?
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
- $charges += $charge;
my $call_details;
my $phonenum = $cust_svc->svc_x->phonenum;
use_disposition_taqua
use_carrierid
use_cdrtypenum
- skip_dst_prefix
skip_dcontext
skip_dstchannel_prefix
- skip_src_length_more noskip_src_length_accountcode_tollfree
skip_dst_length_less
skip_lastapp
);
if length($opt{'use_cdrtypenum'})
&& $cdr->cdrtypenum ne $opt{'use_cdrtypenum'}; #ne otherwise 0 matches ''
- foreach(split(',',$opt{'skip_dst_prefix'})) {
- return "dst starts with '$_'"
- if length($_) && substr($cdr->dst,0,length($_)) eq $_;
- }
-
return "dcontext IN ( $opt{'skip_dcontext'} )"
if $opt{'skip_dcontext'} =~ /\S/
&& grep { $cdr->dcontext eq $_ } split(/\s*,\s*/, $opt{'skip_dcontext'});
return "lastapp is $opt{'skip_lastapp'}"
if length($opt{'skip_lastapp'}) && $cdr->lastapp eq $opt{'skip_lastapp'};
- my $src_length = $opt{'skip_src_length_more'};
- if ( $src_length ) {
-
- if ( $opt{'noskip_src_length_accountcode_tollfree'} ) {
-
- if ( $cdr->is_tollfree('accountcode') ) {
- return "source less than or equal to $src_length digits"
- if length($cdr->src) <= $src_length;
- } else {
- return "source more than $src_length digits"
- if length($cdr->src) > $src_length;
- }
-
- } else {
- return "source more than $src_length digits"
- if length($cdr->src) > $src_length;
- }
-
- }
-
#all right then, rate it
'';
}
$hash->{'country'} = 'US'; # CA is available
- $hash->{'taxable'} = '' if ($hash->{'taxable'} eq 'N');
+ delete($hash->{'taxable'}) if ($hash->{'taxable'} eq 'N');
if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
delete($hash->{actionflag});
+++ /dev/null
-package FS::part_tag;
-
-use strict;
-use base qw( FS::Record );
-use FS::Record qw( qsearch qsearchs );
-
-=head1 NAME
-
-FS::part_tag - Object methods for part_tag records
-
-=head1 SYNOPSIS
-
- use FS::part_tag;
-
- $record = new FS::part_tag \%hash;
- $record = new FS::part_tag { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_tag object represents a tag. FS::part_tag inherits from
-FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item tagnum
-
-primary key
-
-=item tagname
-
-tagname
-
-=item tagdesc
-
-tagdesc
-
-=item tagcolor
-
-tagcolor
-
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new tag. To add the tag to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'part_tag'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid tag. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('tagnum')
- || $self->ut_text('tagname')
- || $self->ut_textn('tagdesc')
- || $self->ut_textn('tagcolor')
- || $self->ut_enum('disabled', [ '', 'Y' ] )
- ;
- return $error if $error;
-
- $self->SUPER::check;
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
my $info = $import_info{$format}
or die "unknown format $format";
- my $job = $param->{'job'};
- $job->update_statustext(0) if $job;
-
my $filetype = $info->{'filetype'}; # CSV or fixed
my @fields = @{ $info->{'fields'}};
my $formatre = $info->{'formatre'}; # for fixed
}
}
- my $num = 0;
foreach (@all_values) {
- if($job) {
- $num++;
- $job->update_statustext(int(100 * $num/scalar(@all_values)));
- }
my @values = @$_;
my %hash;
'custnum' => $custnum,
'payby' => $payby,
'paybatch' => $self->batchnum,
- 'payinfo' => ( $hash{'payinfo'} || $cust_pay_batch->payinfo ),
- map { $_ => $hash{$_} } (qw( paid _date )),
+ map { $_ => $hash{$_} } (qw( paid _date payinfo )),
} );
$error = $cust_pay->insert;
if ( $error ) {
}
-use MIME::Base64;
-use Storable 'thaw';
-use Data::Dumper;
-sub process_import_results {
- my $job = shift;
- my $param = thaw(decode_base64(shift));
- $param->{'job'} = $job;
- warn Dumper($param) if $DEBUG;
- my $batchnum = delete $param->{'batchnum'} or die "no batchnum specified\n";
- my $batch = FS::pay_batch->by_key($batchnum) or die "batchnum '$batchnum' not found\n";
-
- my $file = $param->{'uploaded_files'} or die "no files provided\n";
- $file =~ s/^(\w+):([\.\w]+)$/$2/;
- my $dir = '%%%FREESIDE_CACHE%%%/cache.' . $FS::UID::datasrc;
- open( $param->{'filehandle'},
- '<',
- "$dir/$file" )
- or die "unable to open '$file'.\n";
- my $error = $batch->import_results($param);
- unlink $file;
- die $error if $error;
-}
-
# Formerly httemplate/misc/download-batch.cgi
sub export_batch {
my $self = shift;
This is a mixin class for records that contain payinfo.
+This class handles the following functions for payinfo...
+
+Payment Mask (Generation and Storage)
+Data Validation (parent checks need to be sure to call this)
+Pretty printing
+
=head1 FIELDS
=over 4
sub payinfo {
my($self,$payinfo) = @_;
-
if ( defined($payinfo) ) {
- $self->setfield('payinfo', $payinfo);
- $self->paymask($self->mask_payinfo) unless $payinfo =~ /^99\d{14}$/; #token
+ $self->setfield('payinfo', $payinfo); # This is okay since we are the 'setter'
+ $self->paymask($self->mask_payinfo());
} else {
- $self->getfield('payinfo');
+ $payinfo = $self->getfield('payinfo'); # This is okay since we are the 'getter'
+ return $payinfo;
}
}
sub paymask {
my($self, $paymask) = @_;
- if ( defined($paymask) ) {
- $self->setfield('paymask', $paymask);
+ if ( defined($paymask) && $paymask ne '' ) {
+ # I hate this little bit of magic... I don't expect it to cause a problem,
+ # but who knows... If the payinfo is passed in masked then ignore it and
+ # set it based on the payinfo. The only guy that should call this in this
+ # way is... $self->payinfo
+ $self->setfield('paymask', $self->mask_payinfo());
+
} else {
- $self->getfield('paymask') || $self->mask_payinfo;
+
+ $paymask=$self->getfield('paymask');
+ if (!defined($paymask) || $paymask eq '') {
+ # Generate it if it's blank - Note that we're not going to set it - just
+ # generate
+ $paymask = $self->mask_payinfo();
+ }
+
}
+
+ return $paymask;
}
=back
my $paymask;
if ( $self->is_encrypted($payinfo) ) {
$paymask = 'N/A';
- } elsif ( $payinfo =~ /^99\d{14}$/ || $payinfo eq 'N/A' ) { #token
- $paymask = 'N/A (tokenized)'; #?
} else {
# if not, mask it...
if ($payby eq 'CARD' || $payby eq 'DCRD' || $payby eq 'MCRD') {
$paymask = $payinfo;
}
}
- $paymask;
+ return $paymask;
}
=item payinfo_check
or return "Illegal (mistyped?) credit card number (payinfo)";
$self->payinfo($1);
validate($self->payinfo) or return "Illegal credit card number";
- return "Unknown card type" if $self->payinfo !~ /^99\d{14}$/ #token
- && cardtype($self->payinfo) eq "Unknown";
+ return "Unknown card type" if cardtype($self->payinfo) eq "Unknown";
} else {
$self->payinfo('N/A'); #???
}
=head1 BUGS
+Future items?
+ Encryption - In the Future (Pull from Record.pm)
+ Bad Card Stuff - In the Future (Integrate Banned Pay)
+ Currency - In the Future
+
=head1 SEE ALSO
L<FS::payby>, L<FS::Record>
use FS::queue_arg;
use FS::queue_depend;
use FS::cust_svc;
-use FS::CGI qw(rooturl);
+use FS::CGI qw (rooturl);
@ISA = qw(FS::Record);
@EXPORT_OK = qw( joblisting );
Freeform text status message
-=cut
-
-sub statustext {
- my $self = shift;
- if ( defined ( $_[0] ) ) {
- $self->SUPER::statustext(@_);
- } else {
- my $value = $self->SUPER::statustext();
- my $rooturl = rooturl();
- $value =~ s/%%%ROOTURL%%%/$rooturl/g;
- $value;
- }
-}
-
=item _date
UNIX timestamp
use vars qw($_update_statustext_dbh);
sub update_statustext {
my( $self, $statustext ) = @_;
- return '' if $statustext eq $self->get('statustext'); #avoid rooturl expansion
+ return '' if $statustext eq $self->statustext;
warn "updating statustext for $self to $statustext" if $DEBUG;
$_update_statustext_dbh ||= myconnect;
$sth->execute($statustext, $self->jobnum) or return $sth->errstr;
$_update_statustext_dbh->commit or die $_update_statustext_dbh->errstr;
- $self->set('statustext', $statustext); #avoid rooturl expansion
+ $self->statustext($statustext);
'';
#my $new = new FS::queue { $self->hash };
(see L<FS::rate_detail>), or as a hashref with two keys: I<countrycode>
and I<phonenum>.
-An optional third key, I<weektime>, will return a timed rate (one with
-a non-null I<ratetimenum>) if one exists for a call at that time. If
-no matching timed rate exists, the non-timed rate will be returned.
-
=cut
sub dest_detail {
my $self = shift;
my $regionnum;
- my $weektime;
if ( ref($_[0]) eq 'HASH' ) {
my $countrycode = $_[0]->{'countrycode'};
my $phonenum = $_[0]->{'phonenum'};
- $weektime = $_[0]->{'weektime'};
#find a rate prefix, first look at most specific, then fewer digits,
# finally trying the country code only
} else {
$regionnum = ref($_[0]) ? shift->regionnum : shift;
}
-
- if(!defined($weektime)) {
- return qsearchs( 'rate_detail',
- { 'ratenum' => $self->ratenum,
- 'dest_regionnum' => $regionnum,
- 'ratetimenum' => '',
- } );
- }
- else {
- my @details = grep { my $rate_time = $_->rate_time;
- $rate_time && $rate_time->contains($weektime) }
- qsearch( 'rate_detail',
- { 'ratenum' => $self->ratenum,
- 'dest_regionnum' => $regionnum, } );
- if(!@details) {
- # this may change at some point
- return $self->dest_detail($regionnum);
- }
- elsif(@details == 1) {
- return $details[0];
- }
- else {
- die "overlapping rate_detail times (region $regionnum, time $weektime)\n";
- }
- }
+
+ qsearchs( 'rate_detail', { 'ratenum' => $self->ratenum,
+ 'dest_regionnum' => $regionnum, } );
}
=item rate_detail
use FS::Record qw( qsearch qsearchs dbh );
use FS::rate;
use FS::rate_region;
-use FS::rate_time;
use Tie::IxHash;
@ISA = qw(FS::Record);
=item classnum - usage class (see L<FS::usage_class>) if any for this rate
-=item ratetimenum - rating time period (see L<FS::rate_time) if any
-
=back
=head1 METHODS
$self->dest_region->prefixes_short;
}
-=item rate_time
-
-Returns the L<FS::rate_time> object associated with this call
-plan rate, if there is one.
-
-=cut
-
-sub rate_time {
- my $self = shift;
- $self->ratetimenum ? FS::rate_time->by_key($self->ratetimenum) : ();
-}
-
-=item rate_time_name
-
-Returns the I<ratetimename> field of the L<FS::rate_time> object
-associated with this rate plan.
-
-=cut
-
-sub rate_time_name {
- my $self = shift;
- $self->ratetimenum ? $self->rate_time->ratetimename : '(default)';
-}
-
=item classname
Returns the name of the usage class (see L<FS::usage_class>) associated with
+++ /dev/null
-package FS::rate_time;
-
-use strict;
-use base qw( FS::Record );
-use FS::Record qw( qsearch qsearchs );
-use FS::rate_time_interval;
-
-=head1 NAME
-
-FS::rate_time - Object methods for rate_time records
-
-=head1 SYNOPSIS
-
- use FS::rate_time;
-
- $record = new FS::rate_time \%hash;
- $record = new FS::rate_time { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::rate_time object represents a time period for selection of CDR billing
-rates. FS::rate_time inherits from FS::Record. The following fields are
-currently supported:
-
-=over 4
-
-=item ratetimenum
-
-primary key
-
-=item ratetimename
-
-A label (like "Daytime" or "Weekend").
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new example. To add the example to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'rate_time'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid example. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('ratetimenum')
- || $self->ut_text('ratetimename')
- ;
- return $error if $error;
-
- $self->SUPER::check;
-}
-
-=item intervals
-
-Return the L<FS::rate_time_interval> objects included in this rating period.
-
-=cut
-
-sub intervals {
- my $self = shift;
- return qsearch({ table => 'rate_time_interval',
- hashref => { ratetimenum => $self->ratetimenum },
- order_by => 'ORDER BY stime ASC',
- });
-}
-
-=item contains TIME
-
-Return the L<FS::rate_time_interval> object that contains the specified
-time-of-week (in seconds from the start of Sunday). The primary use of
-this is to test whether that time falls within this rating period.
-
-=cut
-
-sub contains {
- my $self = shift;
- my $weektime = shift;
- return qsearchs('rate_time_interval', { ratetimenum => $self->ratetimenum,
- stime => { op => '<=',
- value => $weektime },
- etime => { op => '>',
- value => $weektime },
- } );
-}
-
-=item description
-
-Returns a list of arrayrefs containing the starting and
-ending times of each interval in this period, in a readable
-format.
-
-=cut
-
-sub description {
- my $self = shift;
- return map { [ $_->description ] } $self->intervals;
-}
-
-
-=back
-
-=head1 BUGS
-
-To be seen.
-
-=head1 SEE ALSO
-
-L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
+++ /dev/null
-package FS::rate_time_interval;
-
-use strict;
-use base qw( FS::Record );
-use FS::Record qw( qsearch qsearchs );
-use List::Util 'first';
-
-=head1 NAME
-
-FS::rate_time_interval - Object methods for rate_time_interval records
-
-=head1 SYNOPSIS
-
- use FS::rate_time_interval;
-
- $record = new FS::rate_time_interval \%hash;
- $record = new FS::rate_time_interval { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::rate_time_interval object represents an interval of clock time during
-the week, such as "Monday, 7 AM to 8 PM". FS::rate_time_interval inherits
-from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item intervalnum
-
-primary key
-
-=item stime
-
-Start of the interval, in seconds from midnight on Sunday.
-
-=item etime
-
-End of the interval.
-
-=item ratetimenum
-
-A foreign key to an L<FS::rate_time> object representing the set of intervals
-to which this belongs.
-
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new example. To add the example to the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'rate_time_interval'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid interval. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('intervalnum')
- || $self->ut_number('stime')
- || $self->ut_number('etime')
- || $self->ut_number('ratetimenum')
- ;
- return $error if $error;
- # Disallow backward intervals. As a special case, an etime of 0
- # should roll to the last second of the week.
- $self->etime(7*24*60*60) if $self->etime == 0;
- return "end of interval is before start" if ($self->etime < $self->stime);
-
- # Detect overlap between intervals within the same rate_time.
- # Since intervals are added one at a time, we only need to look
- # for an existing interval that contains one of the endpoints of
- # this one or that is completely inside this one.
- my $overlap = $self->rate_time->contains($self->stime + 1) ||
- $self->rate_time->contains($self->etime - 1) ||
- first { $self->stime <= $_->stime && $self->etime >= $_->etime }
- ( $self->rate_time->intervals );
- return "interval overlap: (".join('-',$self->description).') with ('.
- join('-',$overlap->description).')' if $overlap;
-
- $self->SUPER::check;
-}
-
-=item rate_time
-
-Returns the L<FS::rate_time> comprising this interval.
-
-=cut
-
-sub rate_time {
- my $self = shift;
- FS::rate_time->by_key($self->ratetimenum);
-}
-
-=item description
-
-Returns two strings containing stime and etime, formatted
-"Day HH:MM AM/PM". Example: "Mon 5:00 AM". Seconds are
-not displayed, so be careful.
-
-=cut
-
-my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
-
-sub description {
- my $self = shift;
- return map {
- sprintf('%s %02d:%02d %s',
- $days[int($_/86400) % 7],
- int($_/3600) % 12,
- int($_/60) % 60,
- (($_/3600) % 24 < 12) ? 'AM' : 'PM' )
- } ( $self->stime, $self->etime );
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::rate_time>, L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
=over 4
-=item cgp_rule
+=item
Returns the rules associated with this service, as FS::cgp_rule objects.
+++ /dev/null
-package FS::svc_CGP_Mixin;
-
-use strict;
-
-=head1 NAME
-
-FS::svc_CGP_Mixin - Mixin class for svc_classes which can be related to cgp_rule
-
-=head1 SYNOPSIS
-
-package FS::svc_table;
-use base qw( FS::svc_CGP_Mixin FS::svc_Common );
-
-=head1 DESCRIPTION
-
-This is a mixin class for svc_ classes that are exported to Communigate Pro.
-
-It currently contains timezone data for domains and accounts.
-
-=head1 METHODS
-
-=over 4
-
-=item cgp_timezone
-
-Returns an arrayref of Communigate time zones.
-
-=cut
-
-#http://www.communigate.com/pub/client/TimeZones.data
-#http://www.communigate.com/cgatepro/WebMail.html#Settings
-
-sub cgp_timezone_values {
- #my $self = shift; #i'm used as a class and object method but just return data
-
- [ '',
- 'HostOS',
- '(+0100) Algeria/Congo',
- '(+0200) Egypt/South Africa',
- '(+0300) Saudi Arabia',
- '(+0400) Oman',
- '(+0500) Pakistan',
- '(+0600) Bangladesh',
- '(+0700) Thailand/Vietnam',
- '(+0800) China/Malaysia',
- '(+0900) Japan/Korea',
- '(+1000) Queensland',
- '(+1100) Micronesia',
- '(+1200) Fiji',
- '(+1300) Tonga/Kiribati',
- '(+1400) Christmas Islands',
- '(-0100) Azores/Cape Verde',
- '(-0200) Fernando de Noronha',
- '(-0300) Argentina/Uruguay',
- '(-0400) Venezuela/Guyana',
- '(-0500) Haiti/Peru',
- '(-0600) Central America',
- '(-0700) Arisona', #Arizona?
- '(-0800) Adamstown',
- '(-0900) Marquesas Islands',
- '(-1000) Hawaii/Tahiti',
- '(-1100) Samoa',
- 'Asia/Afghanistan',
- 'Asia/India',
- 'Asia/Iran',
- 'Asia/Iraq',
- 'Asia/Israel',
- 'Asia/Jordan',
- 'Asia/Lebanon',
- 'Asia/Syria',
- 'Australia/Adelaide',
- 'Australia/East',
- 'Australia/NorthernTerritory',
- 'Europe/Central',
- 'Europe/Eastern',
- 'Europe/Moscow',
- 'Europe/Western',
- 'GMT (+0000)',
- 'Newfoundland',
- 'NewZealand/Auckland',
- 'NorthAmerica/Alaska',
- 'NorthAmerica/Atlantic',
- 'NorthAmerica/Central',
- 'NorthAmerica/Eastern',
- 'NorthAmerica/Mountain',
- 'NorthAmerica/Pacific',
- 'Russia/Ekaterinburg',
- 'Russia/Irkutsk',
- 'Russia/Kamchatka',
- 'Russia/Krasnoyarsk',
- 'Russia/Magadan',
- 'Russia/Novosibirsk',
- 'Russia/Vladivostok',
- 'Russia/Yakutsk',
- 'SouthAmerica/Brasil',
- 'SouthAmerica/Chile',
- 'SouthAmerica/Paraguay',
- ];
-}
-
-=item cgp_emptytrash_values
-
-Returns an arrayref of possible EmptyTrash values.
-
-=cut
-
-#http://www.communigate.com/cgatepro/WebMail.html#Trash
-
-sub cgp_emptytrash_values {
- #my $self = shift; #i'm used as a class and object method but just return data
-
- [ '', #<option value="-1">default(92 days)
- '0 seconds',
- '60 minutes',
- '2 hours',
- '3 hours',
- '6 hours',
- '12 hours',
- '24 hours',
- '2 days',
- '3 days',
- '7 days',
- '10 days',
- '2 weeks',
- '3 weeks',
- '30 days',
- '60 days',
- '90 days',
- '180 days',
- '360 days',
- ];
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-=cut
-
-1;
package FS::svc_acct;
use strict;
-use base qw( FS::svc_Domain_Mixin FS::svc_CGP_Mixin FS::svc_CGPRule_Mixin
- FS::svc_Common );
+use base qw( FS::svc_Domain_Mixin FS::svc_CGPRule_Mixin FS::svc_Common );
use vars qw( $DEBUG $me $conf $skip_fuzzyfiles
$dir_prefix @shells $usernamemin
$usernamemax $passwordmin $passwordmax
disable_select => 1,
},
'cgp_emptytrash' => {
- label => 'Communigate on logout remove trash',
- type => 'select',
- select_list => __PACKAGE__->cgp_emptytrash_values,
+ label => 'Communigate on logout remove trash',
+ type => 'text',
disable_inventory => 1,
disable_select => 1,
},
disable_select => 1,
},
'cgp_timezone' => {
- label => 'Communigate time zone',
- type => 'select',
- select_list => __PACKAGE__->cgp_timezone_values,
+ label => 'Communigate time zone',
+ type => 'select',
+ select_list => [ '',
+ 'HostOS',
+ '(+0100) Algeria/Congo',
+ '(+0200) Egypt/South Africa',
+ '(+0300) Saudi Arabia',
+ '(+0400) Oman',
+ '(+0500) Pakistan',
+ '(+0600) Bangladesh',
+ '(+0700) Thailand/Vietnam',
+ '(+0800) China/Malaysia',
+ '(+0900) Japan/Korea',
+ '(+1000) Queensland',
+ '(+1100) Micronesia',
+ '(+1200) Fiji',
+ '(+1300) Tonga/Kiribati',
+ '(+1400) Christmas Islands',
+ '(-0100) Azores/Cape Verde',
+ '(-0200) Fernando de Noronha',
+ '(-0300) Argentina/Uruguay',
+ '(-0400) Venezuela/Guyana',
+ '(-0500) Haiti/Peru',
+ '(-0600) Central America',
+ '(-0700) Arisona',
+ '(-0800) Adamstown',
+ '(-0900) Marquesas Islands',
+ '(-1000) Hawaii/Tahiti',
+ '(-1100) Samoa',
+ 'Asia/Afghanistan',
+ 'Asia/India',
+ 'Asia/Iran',
+ 'Asia/Iraq',
+ 'Asia/Israel',
+ 'Asia/Jordan',
+ 'Asia/Lebanon',
+ 'Asia/Syria',
+ 'Australia/Adelaide',
+ 'Australia/East',
+ 'Australia/NorthernTerritory',
+ 'Europe/Central',
+ 'Europe/Eastern',
+ 'Europe/Moscow',
+ 'Europe/Western',
+ 'GMT (+0000)',
+ 'Newfoundland',
+ 'NewZealand/Auckland',
+ 'NorthAmerica/Alaska',
+ 'NorthAmerica/Atlantic',
+ 'NorthAmerica/Central',
+ 'NorthAmerica/Eastern',
+ 'NorthAmerica/Mountain',
+ 'NorthAmerica/Pacific',
+ 'Russia/Ekaterinburg',
+ 'Russia/Irkutsk',
+ 'Russia/Kamchatka',
+ 'Russia/Krasnoyarsk',
+ 'Russia/Magadan',
+ 'Russia/Novosibirsk',
+ 'Russia/Vladivostok',
+ 'Russia/Yakutsk',
+ 'SouthAmerica/Brasil',
+ 'SouthAmerica/Chile',
+ 'SouthAmerica/Paraguay',
+ ],
disable_inventory => 1,
disable_select => 1,
},
}
#welcome email
- my $error = '';
- my $msgnum = $conf->config('welcome_msgnum', $agentnum);
- if ( $msgnum ) {
- my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
- $error = $msg_template->send('cust_main' => $cust_main);
+ my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
+ = ('','','','','','');
+
+ if ( $conf->exists('welcome_email', $agentnum) ) {
+ $welcome_template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
+ ) or warn "can't create welcome email template: $Text::Template::ERROR";
+ $welcome_from = $conf->config('welcome_email-from', $agentnum);
+ # || 'your-isp-is-dum'
+ $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
+ || 'Welcome';
+ $welcome_subject_template = new Text::Template (
+ TYPE => 'STRING',
+ SOURCE => $welcome_subject,
+ ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
+ $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
+ || 'text/plain';
}
- else { #!$msgnum
- my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
- = ('','','','','','');
-
- if ( $conf->exists('welcome_email', $agentnum) ) {
- $welcome_template = new Text::Template (
- TYPE => 'ARRAY',
- SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
- ) or warn "can't create welcome email template: $Text::Template::ERROR";
- $welcome_from = $conf->config('welcome_email-from', $agentnum);
- # || 'your-isp-is-dum'
- $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
- || 'Welcome';
- $welcome_subject_template = new Text::Template (
- TYPE => 'STRING',
- SOURCE => $welcome_subject,
- ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
- $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
- || 'text/plain';
- }
- if ( $welcome_template ) {
- my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
- if ( $to ) {
-
- my %hash = (
- 'custnum' => $self->custnum,
- 'username' => $self->username,
- 'password' => $self->_password,
- 'first' => $cust_main->first,
- 'last' => $cust_main->getfield('last'),
- 'pkg' => $cust_pkg->part_pkg->pkg,
- );
- my $wqueue = new FS::queue {
- 'svcnum' => $self->svcnum,
- 'job' => 'FS::svc_acct::send_email'
- };
- my $error = $wqueue->insert(
- 'to' => $to,
- 'from' => $welcome_from,
- 'subject' => $welcome_subject_template->fill_in( HASH => \%hash, ),
- 'mimetype' => $welcome_mimetype,
- 'body' => $welcome_template->fill_in( HASH => \%hash, ),
- );
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error queuing welcome email: $error";
- }
+ if ( $welcome_template && $cust_pkg ) {
+ my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
+ if ( $to ) {
+
+ my %hash = (
+ 'custnum' => $self->custnum,
+ 'username' => $self->username,
+ 'password' => $self->_password,
+ 'first' => $cust_main->first,
+ 'last' => $cust_main->getfield('last'),
+ 'pkg' => $cust_pkg->part_pkg->pkg,
+ );
+ my $wqueue = new FS::queue {
+ 'svcnum' => $self->svcnum,
+ 'job' => 'FS::svc_acct::send_email'
+ };
+ my $error = $wqueue->insert(
+ 'to' => $to,
+ 'from' => $welcome_from,
+ 'subject' => $welcome_subject_template->fill_in( HASH => \%hash, ),
+ 'mimetype' => $welcome_mimetype,
+ 'body' => $welcome_template->fill_in( HASH => \%hash, ),
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error queuing welcome email: $error";
+ }
- if ( $options{'depend_jobnum'} ) {
- warn "$me depend_jobnum found; adding to welcome email dependancies"
+ if ( $options{'depend_jobnum'} ) {
+ warn "$me depend_jobnum found; adding to welcome email dependancies"
+ if $DEBUG;
+ if ( ref($options{'depend_jobnum'}) ) {
+ warn "$me adding jobs ". join(', ', @{$options{'depend_jobnum'}} ).
+ "to welcome email dependancies"
if $DEBUG;
- if ( ref($options{'depend_jobnum'}) ) {
- warn "$me adding jobs ". join(', ', @{$options{'depend_jobnum'}} ).
- "to welcome email dependancies"
- if $DEBUG;
- push @jobnums, @{ $options{'depend_jobnum'} };
- } else {
- warn "$me adding job $options{'depend_jobnum'} ".
- "to welcome email dependancies"
- if $DEBUG;
- push @jobnums, $options{'depend_jobnum'};
- }
+ push @jobnums, @{ $options{'depend_jobnum'} };
+ } else {
+ warn "$me adding job $options{'depend_jobnum'} ".
+ "to welcome email dependancies"
+ if $DEBUG;
+ push @jobnums, $options{'depend_jobnum'};
}
+ }
- foreach my $jobnum ( @jobnums ) {
- my $error = $wqueue->depend_insert($jobnum);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "error queuing welcome email job dependancy: $error";
- }
+ foreach my $jobnum ( @jobnums ) {
+ my $error = $wqueue->depend_insert($jobnum);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error queuing welcome email job dependancy: $error";
}
-
}
- } # if $welcome_template
- } # if !$msgnum
- } # if $cust_pkg
+ }
+
+ }
+
+ } # if ( $cust_pkg )
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
|| $self->ut_snumbern('upbytes')
|| $self->ut_snumbern('downbytes')
|| $self->ut_snumbern('totalbytes')
- || $self->ut_snumbern('seconds_threshold')
- || $self->ut_snumbern('upbytes_threshold')
- || $self->ut_snumbern('downbytes_threshold')
- || $self->ut_snumbern('totalbytes_threshold')
|| $self->ut_enum('_password_encoding', ['',qw(plain crypt ldap)])
|| $self->ut_enum('password_selfchange', [ '', 'Y' ])
|| $self->ut_enum('password_recover', [ '', 'Y' ])
|| $self->ut_enum('cgp_addmailtrailer', [ '', 'Y' ])
#preferences
|| $self->ut_alphasn('cgp_deletemode')
- || $self->ut_enum('cgp_emptytrash', $self->cgp_emptytrash_values)
+ || $self->ut_alphan('cgp_emptytrash')
|| $self->ut_alphan('cgp_language')
|| $self->ut_textn('cgp_timezone')
|| $self->ut_textn('cgp_skinname')
my $reset = 0;
my %handyhash = ();
if ( $options{null} ) {
- %handyhash = ( map { ( $_ => undef, $_."_threshold" => undef ) }
+ %handyhash = ( map { ( $_ => 'NULL', $_."_threshold" => 'NULL' ) }
qw( seconds upbytes downbytes totalbytes )
);
}
#die $error if $error; #services not explicity changed via the UI
my $sql = "UPDATE svc_acct SET " .
- join (',', map { "$_ = ?" } (keys %handyhash) ).
+ join (',', map { "$_ = $handyhash{$_}" } (keys %handyhash) ).
" WHERE svcnum = ". $self->svcnum;
warn "$me $sql\n"
if (scalar(keys %handyhash)) {
my $sth = $dbh->prepare( $sql )
or die "Error preparing $sql: ". $dbh->errstr;
- my $rv = $sth->execute(values %handyhash);
+ my $rv = $sth->execute();
die "Error executing $sql: ". $sth->errstr
unless defined($rv);
die "Can't update usage for svcnum ". $self->svcnum
package FS::svc_domain;
use strict;
-use base qw( FS::svc_Parent_Mixin FS::svc_CGP_Mixin FS::svc_CGPRule_Mixin
- FS::svc_Common );
+use base qw( FS::svc_Parent_Mixin FS::svc_CGPRule_Mixin FS::svc_Common );
use vars qw( $whois_hack $conf
@defaultrecords $soadefaultttl $soaemail $soaexpire $soamachine
$soarefresh $soaretry
},
'acct_def_cgp_emptytrash' => {
label => 'Acct. default Communigate on logout remove trash',
- type => 'select',
- select_list => __PACKAGE__->cgp_emptytrash_values,
+ type => 'text',
disable_inventory => 1,
disable_select => 1,
},
disable_select => 1,
},
'acct_def_cgp_timezone' => {
- label => 'Acct. default time zone',
- type => 'select',
- select_list => __PACKAGE__->cgp_timezone_values,
+ label => 'Acct. default time zone',
+ type => 'select',
+ select_list => [ '',
+ 'HostOS',
+ '(+0100) Algeria/Congo',
+ '(+0200) Egypt/South Africa',
+ '(+0300) Saudi Arabia',
+ '(+0400) Oman',
+ '(+0500) Pakistan',
+ '(+0600) Bangladesh',
+ '(+0700) Thailand/Vietnam',
+ '(+0800) China/Malaysia',
+ '(+0900) Japan/Korea',
+ '(+1000) Queensland',
+ '(+1100) Micronesia',
+ '(+1200) Fiji',
+ '(+1300) Tonga/Kiribati',
+ '(+1400) Christmas Islands',
+ '(-0100) Azores/Cape Verde',
+ '(-0200) Fernando de Noronha',
+ '(-0300) Argentina/Uruguay',
+ '(-0400) Venezuela/Guyana',
+ '(-0500) Haiti/Peru',
+ '(-0600) Central America',
+ '(-0700) Arisona',
+ '(-0800) Adamstown',
+ '(-0900) Marquesas Islands',
+ '(-1000) Hawaii/Tahiti',
+ '(-1100) Samoa',
+ 'Asia/Afghanistan',
+ 'Asia/India',
+ 'Asia/Iran',
+ 'Asia/Iraq',
+ 'Asia/Israel',
+ 'Asia/Jordan',
+ 'Asia/Lebanon',
+ 'Asia/Syria',
+ 'Australia/Adelaide',
+ 'Australia/East',
+ 'Australia/NorthernTerritory',
+ 'Europe/Central',
+ 'Europe/Eastern',
+ 'Europe/Moscow',
+ 'Europe/Western',
+ 'GMT (+0000)',
+ 'Newfoundland',
+ 'NewZealand/Auckland',
+ 'NorthAmerica/Alaska',
+ 'NorthAmerica/Atlantic',
+ 'NorthAmerica/Central',
+ 'NorthAmerica/Eastern',
+ 'NorthAmerica/Mountain',
+ 'NorthAmerica/Pacific',
+ 'Russia/Ekaterinburg',
+ 'Russia/Irkutsk',
+ 'Russia/Kamchatka',
+ 'Russia/Krasnoyarsk',
+ 'Russia/Magadan',
+ 'Russia/Novosibirsk',
+ 'Russia/Vladivostok',
+ 'Russia/Yakutsk',
+ 'SouthAmerica/Brasil',
+ 'SouthAmerica/Chile',
+ 'SouthAmerica/Paraguay',
+ ],
disable_inventory => 1,
disable_select => 1,
},
#XXX archive messages
#preferences
|| $self->ut_alphasn('acct_def_cgp_deletemode')
- || $self->ut_enum('acct_def_cgp_emptytrash',
- $self->cgp_emptytrash_values )
+ || $self->ut_alphan('acct_def_cgp_emptytrash')
|| $self->ut_alphan('acct_def_cgp_language')
|| $self->ut_textn('acct_def_cgp_timezone')
|| $self->ut_textn('acct_def_cgp_skinname')
use strict;
use base qw( FS::svc_External_Common );
use FS::Record qw( qsearch qsearchs dbh );
-use FS::Conf;
use FS::cust_svc;
use FS::svc_phone;
use FS::svc_acct;
Maximum number of extensions
-=item max_simultaneous
-
-Maximum number of simultaneous users
-
=back
=head1 METHODS
'id' => 'ID',
'title' => 'Name',
'max_extensions' => 'Maximum number of User Extensions',
- 'max_simultaneous' => 'Maximum number of simultaneous users',
# 'field' => 'Description',
# 'another_field' => {
# 'label' => 'Description',
=cut
-#sub replace {
-# my ( $new, $old ) = ( shift, shift );
-# my $error;
-#
-# $error = $new->SUPER::replace($old);
-# return $error if $error;
-#
-# '';
-#}
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+ my $error;
+
+ $error = $new->SUPER::replace($old);
+ return $error if $error;
+
+ '';
+}
=item suspend
sub _check_duplicate {
my $self = shift;
- my $conf = new FS::Conf;
- return '' if $conf->config('global_unique-pbx_title') eq 'disabled';
-
$self->lock_table;
if ( qsearchs( 'svc_pbx', { 'title' => $self->title } ) ) {
}
}
-=item get_cdrs
-
-Returns a set of Call Detail Records (see L<FS::cdr>) associated with this
-service. By default, "associated with" means that the "charged_party" field of
-the CDR matches the "title" field of the service.
-
-=over 2
-
-Accepts the following options:
-
-=item for_update => 1: SELECT the CDRs "FOR UPDATE".
-
-=item status => "" (or "done"): Return only CDRs with that processing status.
-
-=item inbound => 1: No-op for svc_pbx CDR processing.
-
-=item default_prefix => "XXX": Also accept the phone number of the service prepended
-with the chosen prefix.
-
-=item disable_src => 1: No-op for svc_pbx CDR processing.
-
-=back
-
-=cut
-
-sub get_cdrs {
- my($self, %options) = @_;
- my %hash = ();
- my @where = ();
-
- my @fields = ( 'charged_party' );
- $hash{'freesidestatus'} = $options{'status'}
- if exists($options{'status'});
-
- my $for_update = $options{'for_update'} ? 'FOR UPDATE' : '';
-
- my $title = $self->title;
-
- my $prefix = $options{'default_prefix'};
-
- my @orwhere = map " $_ = '$title' ", @fields;
- push @orwhere, map " $_ = '$prefix$title' ", @fields
- if length($prefix);
- if ( $prefix =~ /^\+(\d+)$/ ) {
- push @orwhere, map " $_ = '$1$title' ", @fields
- }
-
- push @where, ' ( '. join(' OR ', @orwhere ). ' ) ';
-
- if ( $options{'begin'} ) {
- push @where, 'startdate >= '. $options{'begin'};
- }
- if ( $options{'end'} ) {
- push @where, 'startdate < '. $options{'end'};
- }
-
- my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where );
-
- my @cdrs =
- qsearch( {
- 'table' => 'cdr',
- 'hashref' => \%hash,
- 'extra_sql' => $extra_sql,
- 'order_by' => "ORDER BY startdate $for_update",
- } );
-
- @cdrs;
-}
-
=back
=head1 BUGS
use FS::cust_main;
use FS::Misc qw( csv_from_fixed );
+#i'd like to dump these
+use FS::CGI qw(rooturl popurl);
use URI::Escape;
@ISA = qw( FS::Record );
unless ( $part_pkg_taxproduct ) {
return "failed to find part_pkg_taxproduct (".
- $taxproduct->{$pkgpart}->{$class}. ") for pkgpart $pkgpart\n";
+ $taxproduct->{pkgpart}->{$class}. ") for pkgpart $pkgpart\n";
}
if ( $class eq '' ) {
=cut
sub queue_liability_report {
- my $job = shift;
- my $param = thaw(decode_base64(shift));
-
- my $cgi = new CGI;
- $cgi->param('beginning', $param->{beginning});
- $cgi->param('ending', $param->{ending});
+ my $cgi = shift;
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
- my $agentnum = $param->{agentnum};
- if ($agentnum =~ /^(\d+)$/) { $agentnum = $1; } else { $agentnum = ''; };
- generate_liability_report(
+ my $agentnum = $cgi->param('agentnum');
+ $agentnum =~ /^(\d+)$/ ? $agentnum = $1 : $agentnum = '';
+ my $job = new FS::queue { job => 'FS::tax_rate::generate_liability_report' };
+ $job->insert(
'beginning' => $beginning,
'ending' => $ending,
'agentnum' => $agentnum,
- 'p' => $param->{RootURL},
- 'job' => $job,
+ 'p' => popurl(2),
+ 'rooturl' => rooturl,
);
}
sub generate_liability_report {
my %args = @_;
- my ( $count, $last, $min_sec ) = _progressbar_foo();
-
#let us open the temp file early
my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc;
my $report = new File::Temp( TEMPLATE => 'report.tax.liability.XXXXXXXX',
my $credit = 0;
my %taxes = ();
my %basetaxes = ();
- my $calculated = 0;
- my @tax_and_location = qsearch({ table => 'cust_bill_pkg',
- select => $select,
- hashref => { pkgpart => 0 },
- addl_from => $addl_from,
- extra_sql => $where,
- });
- $count = scalar(@tax_and_location);
- foreach my $t ( @tax_and_location ) {
-
- if ( $args{job} ) {
- if ( time - $min_sec > $last ) {
- $args{job}->update_statustext( int( 100 * $calculated / $count ).
- ",Calculated"
- );
- $last = time;
- }
- }
-
+ foreach my $t (qsearch({ table => 'cust_bill_pkg',
+ select => $select,
+ hashref => { pkgpart => 0 },
+ addl_from => $addl_from,
+ extra_sql => $where,
+ })
+ )
+ {
my @params = map { my $f = $_; $f =~ s/.*\.//; $f } @taxparam;
my $label = join('~', map { $t->$_ } @params);
$label = 'Tax'. $label if $label =~ /^~/;
#ordering
-
- if ( $args{job} ) {
- $args{job}->update_statustext( "0,Sorted" );
- $last = time;
- }
-
my @taxes = ();
foreach my $tax ( sort { $a cmp $b } keys %taxes ) {
my $bgcolor2 = '#ffffff';
my $bgcolor = '';
- $count = scalar(@taxes);
- $calculated = 0;
foreach my $tax ( @taxes ) {
- if ( $args{job} ) {
- if ( time - $min_sec > $last ) {
- $args{job}->update_statustext( int( 100 * $calculated / $count ).
- ",Generated"
- );
- $last = time;
- }
- }
-
if ( $bgcolor eq $bgcolor1 ) {
$bgcolor = $bgcolor2;
} else {
print $report <<EOF;
<TR>
<TD CLASS="grid" BGCOLOR="<% '$bgcolor' %>"><% '$tax->{label}' %></TD>
- <% ($tax->{base}) ? qq!<TD CLASS="grid" BGCOLOR="$bgcolor"></TD>! : '' %>
+ <% $tax->{base} ? qq!<TD CLASS="grid" BGCOLOR="$bgcolor"></TD>! : '' %>
<TD CLASS="grid" BGCOLOR="<% '$bgcolor' %>" ALIGN="right">
<A HREF="<% '$baselink$link' %>;istax=1"><% '$money_char' %><% sprintf('%.2f', $tax->{'tax'} ) %></A>
</TD>
<% !($tax->{base}) ? qq!<TD CLASS="grid" BGCOLOR="$bgcolor"></TD>! : '' %>
<TD CLASS="grid" BGCOLOR="<% '$bgcolor' %>"></TD>
- <% ($tax->{base}) ? qq!<TD CLASS="grid" BGCOLOR="$bgcolor"></TD>! : '' %>
+ <% $tax->{base} ? qq!<TD CLASS="grid" BGCOLOR="$bgcolor"></TD>! : '' %>
<TD CLASS="grid" BGCOLOR="<% '$bgcolor' %>" ALIGN="right">
<A HREF="<% '$baselink$link' %>;istax=1;iscredit=rate"><% '$money_char' %><% sprintf('%.2f', $tax->{'credit'} ) %></A>
</TD>
my $dropstring = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/report.';
$reportname =~ s/^$dropstring//;
- my $reporturl = "%%%ROOTURL%%%/misc/queued_report?report=$reportname";
+ my $reporturl = $args{rooturl}. "/misc/queued_report?report=$reportname";
die "<a href=$reporturl>view</a>\n";
}
use strict;
use vars qw( @ISA );
use FS::Record qw( qsearch qsearchs );
-use FS::Conf;
-
-my $conf = new FS::Conf;
@ISA = qw(FS::Record);
sub { shift->{description} },
sub { shift->{calls} },
sub { sprintf( '%.1f', shift->{duration}/60 ) },
- sub { my($href, %opt) = @_;
- ($opt{dollar} || ''). $href->{amount};
- },
+ sub { shift->{amount} },
],
'align' => [ qw( l r r r ) ],
'span' => [ qw( 4 1 1 1 ) ], # unitprices?
'width' => [ qw( 8.2cm 2.5cm 1.4cm 1.6cm ) ], # don't like this
- 'show' => 1,
},
'simpler' => {
'label' => [ qw( Description Calls Amount ) ],
'fields' => [
sub { shift->{description} },
sub { shift->{calls} },
- sub { my($href, %opt) = @_;
- ($opt{dollar} || ''). $href->{amount};
- },
+ sub { shift->{amount} },
],
'align' => [ qw( l r r ) ],
'span' => [ qw( 5 1 1 ) ],
'width' => [ qw( 10.7cm 1.4cm 1.6cm ) ], # don't like this
- 'show' => 1,
},
'usage_simple' => {
'label' => [ qw( Date Time Number Destination Duration Amount ) ],
sub { ' ' },
sub { ' ' },
sub { ' ' },
- sub { my $href = shift; #ugh! making bunk of 'normalization'
- $href->{subtotal} ? $href->{subtotal} : ' '
- },
- ],
- 'align' => [ qw( l l l l r r ) ],
- 'span' => [ qw( 1 1 1 1 1 2 ) ], # unitprices?
- 'width' => [ qw( 4.3cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this
- 'show' => 0,
- },
- 'usage_6col' => {
- 'label' => [ qw( col1 col2 col3 col4 col5 col6 ) ],
- 'fields' => [
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
sub { ' ' },
- sub { my $href = shift; #ugh! making bunk of 'normalization'
- $href->{subtotal} ? $href->{subtotal} : ' '
- },
],
'align' => [ qw( l l l l r r ) ],
- 'span' => [ qw( 1 1 1 1 1 2 ) ], # unitprices?
+ 'span' => [ qw( 2 1 1 1 1 1 ) ], # unitprices?
'width' => [ qw( 4.3cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this
- 'show' => 0,
- },
- 'usage_7col' => {
- 'label' => [ qw( col1 col2 col3 col4 col5 col6 col7 ) ],
- 'fields' => [
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
- sub { ' ' },
- sub { my $href = shift; #ugh! making bunk of 'normalization'
- $href->{subtotal} ? $href->{subtotal} : ' '
- },
- ],
- 'align' => [ qw( l l l l l r r ) ],
- 'span' => [ qw( 1 1 1 1 1 1 1 ) ], # unitprices?
- 'width' => [ qw( 2.9cm 1.4cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this
- 'show' => 0,
},
);
sub summary_formats_labelhash {
- map { $_ => join(',', @{$summary_formats{$_}{label}}) }
- grep { $summary_formats{$_}{show} }
- keys %summary_formats;
+ map { $_ => join(',', @{$summary_formats{$_}{label}}) } keys %summary_formats;
}
=item header_generator FORMAT
);
sub _generator_defaults {
- my ( $self, $format, %opt ) = @_;
- my %format = ( %{ $summary_formats{$self->format} }, %opt );
- return ( \%format, ' ', ' ', ' ', sub { shift } );
+ my ( $self, $format ) = ( shift, shift );
+ return ( $summary_formats{$self->format}, ' ', ' ', ' ', sub { shift } );
}
sub header_generator {
- my ( $self, $format, %opt ) = @_;
+ my ( $self, $format ) = ( shift, shift );
my ( $f, $prefix, $suffix, $separator, $column ) =
- $self->_generator_defaults($format, %opt);
+ $self->_generator_defaults($format);
if ($format eq 'latex') {
$prefix = "\\hline\n\\rule{0pt}{2.5ex}\n\\makebox[1.4cm]{}&\n";
my @args = @_;
my @result = ();
- foreach (my $i = 0; exists($f->{label}->[$i]); $i++) {
+ foreach (my $i = 0; $f->{label}->[$i]; $i++) {
push @result,
&{$column}( map { $f->{$_}->[$i] } qw(label align span width) );
}
=cut
sub description_generator {
- my ( $self, $format, %opt ) = @_;
+ my ( $self, $format ) = ( shift, shift );
my ( $f, $prefix, $suffix, $separator, $column ) =
- $self->_generator_defaults($format, %opt);
+ $self->_generator_defaults($format);
- my $money_char = '$';
if ($format eq 'latex') {
$prefix = "\\hline\n\\multicolumn{1}{c}{\\rule{0pt}{2.5ex}~} &\n";
$suffix = '\\\\';
sub { my ($d,$a,$s,$w) = @_;
return "\\multicolumn{$s}{$a}{\\makebox[$w][$a]{\\textbf{$d}}}";
};
- $money_char = '\\dollar';
}elsif ( $format eq 'html' ) {
$prefix = '"><td align="center"></td>';
$suffix = '';
sub { my ($d,$a,$s,$w) = @_;
return qq!<td align="$html_align{$a}">$d</td>!;
};
- $money_char = $conf->config('money_char') || '$';
}
sub {
- #my @args = @_;
- my ($href) = shift;
+ my @args = @_;
my @result = ();
foreach (my $i = 0; $f->{label}->[$i]; $i++) {
- my $dollar = '';
- $dollar = $money_char if $i == scalar(@{$f->{label}})-1;
- push @result,
- &{$column}( &{$f->{fields}->[$i]}($href, 'dollar' => $dollar),
- map { $f->{$_}->[$i] } qw(align span width)
- );
+ push @result, &{$column}( &{$f->{fields}->[$i]}(@args),
+ map { $f->{$_}->[$i] } qw(align span width)
+ );
}
$prefix. join( $separator, @result ). $suffix;
=cut
sub total_generator {
- my ( $self, $format, %opt ) = @_;
+ my ( $self, $format ) = ( shift, shift );
# $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .
# '{' . $section->{'subtotal'} . '}' . "\n";
my ( $f, $prefix, $suffix, $separator, $column ) =
- $self->_generator_defaults($format, %opt);
+ $self->_generator_defaults($format);
my $style = '';
if ($format eq 'latex') {
# total_item and amount vs total_amount -- another array of functions?
sub total_line_generator {
- my ( $self, $format, %opt ) = @_;
+ my ( $self, $format ) = ( shift, shift );
# $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .
# '{' . $line->{'total_amount'} . '}' . "\n";
my ( $f, $prefix, $suffix, $separator, $column ) =
- $self->_generator_defaults($format, %opt);
+ $self->_generator_defaults($format);
my $style = '';
if ($format eq 'latex') {
bin/freeside-radgroup
bin/freeside-reexport
bin/freeside-selfservice-server
-bin/freeside-selfservice-xmlrpcd
bin/freeside-setinvoice
bin/freeside-setup
bin/freeside-sqlradius-radacctd
FS/InitHandler.pm
FS/ClientAPI.pm
FS/ClientAPI_SessionCache.pm
-FS/ClientAPI_XMLRPC.pm
FS/ClientAPI/passwd.pm
-FS/ClientAPI/Agent.pm
-FS/ClientAPI/Bulk.pm
-FS/ClientAPI/MasonComponent.pm
FS/ClientAPI/MyAccount.pm
-FS/ClientAPI/PrepaidPhone.pm
-FS/ClientAPI/SGNG.pm
-FS/ClientAPI/Signup.pm
FS/Conf.pm
FS/ConfItem.pm
FS/Cron/backup.pm
FS/Cron/bill.pm
FS/Cron/vacuum.pm
FS/Daemon.pm
-FS/Maestro.pm
FS/Misc.pm
FS/Record.pm
FS/Report.pm
t/cgp_rule_condition.t
FS/cgp_rule_action.pm
t/cgp_rule_action.t
-FS/msg_template.pm
-t/msg_template.t
-FS/cust_tag.pm
-t/cust_tag.t
-FS/part_tag.pm
-t/part_tag.t
-FS/svc_CGP_Mixin.pm
-FS/svc_CGPRule_Mixin.pm
# parse command line
###
-use vars qw( $opt_m $opt_p $opt_r $opt_e $opt_d $opt_v $opt_P $opt_a );
-getopts('m:p:r:e:d:v:P:a');
+use vars qw( $opt_m $opt_p $opt_r $opt_e $opt_d $opt_v );
+getopts('m:p:r:e:d:v');
$opt_e ||= 'csv';
#$opt_e = ".$opt_e" unless $opt_e =~ /^\./;
$opt_p ||= '';
-my %options = ();
-
my $user = shift or die &usage;
adminsuidsetup $user;
# %%%FREESIDE_CACHE%%%
-my $cachedir = '%%%FREESIDE_CACHE%%%/cache.'. datasrc. '/cdrs';
+my $cachedir = '/usr/local/etc/freeside/cache.'. datasrc. '/cdrs';
mkdir $cachedir unless -d $cachedir;
my $format = shift or die &usage;
my $ls;
if($opt_m eq 'ftp') {
- $options{'Port'} = $opt_P if $opt_P;
- $options{'Debug'} = $opt_v if $opt_v;
- $options{'Passive'} = $opt_a if $opt_a;
-
my $ls_ftp = ftp();
$ls = [ grep { /^$opt_p.*\.$opt_e$/i } $ls_ftp->ls ];
}
elsif($opt_m eq 'sftp') {
- $options{'port'} = $opt_P if $opt_P;
- $options{'debug'} = $opt_v if $opt_v;
-
my $ls_sftp = sftp();
$ls_sftp->setcwd($opt_r) or die "can't chdir to $opt_r\n"
my ($hostname, $user) = reverse split('@', $servername);
my ($user, $pass) = split(':', $user);
- my $ftp = Net::FTP->new($hostname, %options)
- or die "FTP connection to '$hostname' failed.";
+ my $ftp = Net::FTP->new($hostname) or die "FTP connection to '$hostname' failed.";
$ftp->login($user, $pass) or die "FTP login failed: ".$ftp->message;
$ftp->cwd($opt_r) or die "can't chdir to $opt_r\n" if $opt_r;
return $ftp;
=head1 SYNOPSIS
- cdr.sftp_and_import [ -m method ] [ -p prefix ] [ -e extension ]
- [ -r remotefolder ] [ -d donefolder ] [ -v level ] [ -P port ]
- [ -a ] user format [sftpuser@]servername
+ cdr.sftp_and_import [ -m method ][ -p prefix ] [ -e extension ] [ -r remotefolder ] [ -d donefolder ] [ -v ] user format [sftpuser@]servername
=head1 DESCRIPTION
-Command line tool to download CDR files from a remote server via SFTP
-or FTP and then import them into the database.
+Command line tool to download CDR files from a remote server via SFTP or FTP and then
+import them into the database.
-m: transfer method (sftp or ftp), defaults to sftp
-d: if specified, moves files to the specified folder when done
--P: if specified, sets the port to use
-
--a: use ftp passive mode
-
--v: set verbosity level; this script only has one level, but it will
- be passed as the 'debug' argument to the transport method
+-v: verbose
user: freeside username
-#!/usr/bin/perl -w
+#!/usr/bin/perl -Tw
use strict;
use DBI;
-#!/usr/bin/perl -w
+#!/usr/bin/perl -Tw
use strict;
use LWP::UserAgent;
+++ /dev/null
-#!/usr/bin/perl
-#
-# based on http://www.perlmonks.org/?node_id=582781 by Justin Hawkins
-# and http://poe.perl.org/?POE_Cookbook/Web_Server_With_Forking
-
-###
-# modules and constants and variables, oh my
-###
-
-use warnings;
-use strict;
-
-use constant DEBUG => 1; # Enable much runtime information.
-use constant MAX_PROCESSES => 10; # Total server process count.
-use constant SERVER_PORT => 8080; # Server port.
-use constant TESTING_CHURN => 0; # Randomly test process respawning.
-
-use POE 1.2; # Base features.
-use POE::Filter::HTTPD; # For serving HTTP content.
-use POE::Wheel::ReadWrite; # For socket I/O.
-use POE::Wheel::SocketFactory; # For serving socket connections.
-
-use XMLRPC::Transport::HTTP; #SOAP::Transport::HTTP;
-use XMLRPC::Lite; # for XMLRPC::Serializer
-
-use FS::Daemon qw( daemonize1 drop_root logfile daemonize2 );
-use FS::UID qw( adminsuidsetup forksuidsetup dbh );
-use FS::Conf;
-use FS::ClientAPI qw( load_clientapi_modules );
-use FS::ClientAPI_XMLRPC; #FS::SelfService::XMLRPC;
-
-#freeside
-my $FREESIDE_LOG = "%%%FREESIDE_LOG%%%";
-my $FREESIDE_LOCK = "%%%FREESIDE_LOCK%%%";
-my $lock_file = "$FREESIDE_LOCK/selfservice-xmlrpcd.writelock";
-
-#freeside xmlrpc.cgi
-my %typelookup = (
- base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'],
- dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'],
- string => [40, sub {1}, 'as_string'],
-);
-
-###
-# freeside init
-###
-
-my $user = shift or die &usage;
-
-$FS::Daemon::NOSIG = 1;
-$FS::Daemon::PID_NEWSTYLE = 1;
-daemonize1('selfservice-xmlrpcd');
-
-POE::Kernel->has_forked(); #daemonize forks...
-
-drop_root();
-
-adminsuidsetup($user);
-
-load_clientapi_modules;
-
-logfile("$FREESIDE_LOG/selfservice-xmlrpcd.log");
-
-daemonize2();
-
-my $conf = new FS::Conf;
-
-die "not running; selfservice-xmlrpc conf option is off\n"
- unless $conf->exists('selfservice-xmlrpc');
-
-#parent doesn't need to hold a DB connection open
-dbh->disconnect;
-undef $FS::UID::dbh;
-
-###
-# the main loop
-###
-
-server_spawn(MAX_PROCESSES);
-POE::Kernel->run();
-exit;
-
-###
-# the subroutines
-###
-
-### Spawn the main server. This will run as the parent process.
-
-sub server_spawn {
- my ($max_processes) = @_;
-
- POE::Session->create(
- inline_states => {
- _start => \&server_start,
- _stop => \&server_stop,
- do_fork => \&server_do_fork,
- got_error => \&server_got_error,
- got_sig_int => \&server_got_sig_int,
- got_sig_child => \&server_got_sig_child,
- got_connection => \&server_got_connection,
- _child => sub { undef },
- },
- heap => { max_processes => MAX_PROCESSES },
- );
-}
-
-### The main server session has started. Set up the server socket and
-### bookkeeping information, then fork the initial child processes.
-
-sub server_start {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
-
- $heap->{server} = POE::Wheel::SocketFactory->new
- ( BindPort => SERVER_PORT,
- SuccessEvent => "got_connection",
- FailureEvent => "got_error",
- Reuse => "yes",
- );
-
- $kernel->sig( INT => "got_sig_int" );
- $kernel->sig( TERM => "got_sig_int" ); #huh
-
- $heap->{children} = {};
- $heap->{is_a_child} = 0;
-
- warn "Server $$ has begun listening on port ", SERVER_PORT, "\n";
-
- $kernel->yield("do_fork");
-}
-
-### The server session has shut down. If this process has any
-### children, signal them to shutdown too.
-
-sub server_stop {
- my $heap = $_[HEAP];
- DEBUG and warn "Server $$ stopped.\n";
-
- if ( my @children = keys %{ $heap->{children} } ) {
- DEBUG and warn "Server $$ is signaling children to stop.\n";
- kill INT => @children;
- }
-}
-
-### The server session has encountered an error. Shut it down.
-
-sub server_got_error {
- my ( $heap, $syscall, $errno, $error ) = @_[ HEAP, ARG0 .. ARG2 ];
- warn( "Server $$ got $syscall error $errno: $error\n",
- "Server $$ is shutting down.\n",
- );
- delete $heap->{server};
-}
-
-### The server has a need to fork off more children. Only honor that
-### request form the parent, otherwise we would surely "forkbomb".
-### Fork off as many child processes as we need.
-
-sub server_do_fork {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
-
- return if $heap->{is_a_child};
-
- #my $current_children = keys %{ $heap->{children} };
- #for ( $current_children + 2 .. $heap->{max_processes} ) {
- while (scalar(keys %{$heap->{children}}) < $heap->{max_processes}) {
-
- DEBUG and warn "Server $$ is attempting to fork.\n";
-
- my $pid = fork();
-
- unless ( defined($pid) ) {
- DEBUG and
- warn( "Server $$ fork failed: $!\n",
- "Server $$ will retry fork shortly.\n",
- );
- $kernel->delay( do_fork => 1 );
- return;
- }
-
- # Parent. Add the child process to its list.
- if ($pid) {
- $heap->{children}->{$pid} = 1;
- $kernel->sig_child($pid, "got_sig_child");
- next;
- }
-
- # Child. Clear the child process list.
- $kernel->has_forked();
- DEBUG and warn "Server $$ forked successfully.\n";
- $heap->{is_a_child} = 1;
- $heap->{children} = {};
-
- #freeside db connection, etc.
- forksuidsetup($user);
-
- return;
- }
-}
-
-### The server session received SIGINT. Don't handle the signal,
-### which in turn will trigger the process to exit gracefully.
-
-sub server_got_sig_int {
- my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
- DEBUG and warn "Server $$ received SIGINT/TERM.\n";
-
- if ( my @children = keys %{ $heap->{children} } ) {
- DEBUG and warn "Server $$ is signaling children to stop.\n";
- kill INT => @children;
- }
-
- delete $heap->{server};
- $kernel->sig_handled();
-}
-
-### The server session received a SIGCHLD, indicating that some child
-### server has gone away. Remove the child's process ID from our
-### list, and trigger more fork() calls to spawn new children.
-
-sub server_got_sig_child {
- my ( $kernel, $heap, $child_pid ) = @_[ KERNEL, HEAP, ARG1 ];
-
- return unless delete $heap->{children}->{$child_pid};
-
- DEBUG and warn "Server $$ reaped child $child_pid.\n";
- $kernel->yield("do_fork") if exists $_[HEAP]->{server};
-}
-
-### The server session received a connection request. Spawn off a
-### client handler session to parse the request and respond to it.
-
-sub server_got_connection {
- my ( $heap, $socket, $peer_addr, $peer_port ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
-
- DEBUG and warn "Server $$ received a connection.\n";
-
- POE::Session->create(
- inline_states => {
- _start => \&client_start,
- _stop => \&client_stop,
- got_request => \&client_got_request,
- got_flush => \&client_flushed_request,
- got_error => \&client_got_error,
- _parent => sub { 0 },
- },
- heap => {
- socket => $socket,
- peer_addr => $peer_addr,
- peer_port => $peer_port,
- },
- );
-
- # Gracefully exit if testing process churn.
- delete $heap->{server}
- if TESTING_CHURN and $heap->{is_a_child} and ( rand() < 0.1 );
-}
-
-### The client handler has started. Wrap its socket in a ReadWrite
-### wheel to begin interacting with it.
-
-sub client_start {
- my $heap = $_[HEAP];
-
- $heap->{client} = POE::Wheel::ReadWrite->new
- ( Handle => $heap->{socket},
- Filter => POE::Filter::HTTPD->new(),
- InputEvent => "got_request",
- ErrorEvent => "got_error",
- FlushedEvent => "got_flush",
- );
-
- DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " started.\n";
-}
-
-### The client handler has stopped. Log that fact.
-
-sub client_stop {
- DEBUG and warn "Client handler $$/", $_[SESSION]->ID, " stopped.\n";
-}
-
-### The client handler has received a request. If it's an
-### HTTP::Response object, it means some error has occurred while
-### parsing the request. Send that back and return immediately.
-### Otherwise parse and process the request, generating and sending an
-### HTTP::Response object in response.
-
-sub client_got_request {
- my ( $heap, $request ) = @_[ HEAP, ARG0 ];
-
- forksuidsetup($user) unless dbh && dbh->ping;
-
- my $serializer = new XMLRPC::Serializer(typelookup => \%typelookup);
-
- #my $soap = SOAP::Transport::HTTP::Server
- my $soap = XMLRPC::Transport::HTTP::Server
- -> new
- -> dispatch_to('FS::ClientAPI_XMLRPC')
- -> serializer($serializer);
-
- DEBUG and
- warn "Client handler $$/", $_[SESSION]->ID, " is handling a request.\n";
-
- if ( $request->isa("HTTP::Response") ) {
- $heap->{client}->put($request);
- return;
- }
-
- $soap->request($request);
- $soap->handle;
- my $response = $soap->response;
-
- $heap->{client}->put($response);
-}
-
-### The client handler received an error. Stop the ReadWrite wheel,
-### which also closes the socket.
-
-sub client_got_error {
- my ( $heap, $operation, $errnum, $errstr ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
- DEBUG and
- warn( "Client handler $$/", $_[SESSION]->ID,
- " got $operation error $errnum: $errstr\n",
- "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
- );
- delete $heap->{client};
-}
-
-### The client handler has flushed its response to the socket. We're
-### done with the client connection, so stop the ReadWrite wheel.
-
-sub client_flushed_request {
- my $heap = $_[HEAP];
- DEBUG and
- warn( "Client handler $$/", $_[SESSION]->ID,
- " flushed its response.\n",
- "Client handler $$/", $_[SESSION]->ID, " is shutting down.\n"
- );
- delete $heap->{client};
-}
-
-sub usage {
- die "Usage:\n\n freeside-selfservice-xmlrpcd user\n";
-}
-
-###
-# the end
-###
-
-1;
-#!/usr/bin/perl -w
+#!/usr/bin/perl -Tw
#to delay loading dbdef until we're ready
BEGIN { $FS::Schema::setup_hack = 1; }
$FS::part_pkg::setup_hack = 1;
use strict;
-use vars qw($opt_u $opt_d $opt_v $opt_q);
+use vars qw($opt_u $opt_d $opt_v);
use Getopt::Std;
use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets);
use FS::CurrentUser;
#my %attrib2db =
# map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
-getopts("u:vqd:");
-$opt_v = 1 unless $opt_q; #verbose by default now
-
+getopts("u:vd:");
my $config_dir = shift || '%%%DIST_CONF%%%' ;
$config_dir =~ /^([\w.:=\/]+)$/
or die "unacceptable configuration directory name";
# create a dbdef object from the old data structure
###
-warn "Loading schema objects\n" if $opt_v;
-
my $dbdef = dbdef_dist(datasrc);
#important
# create 'em
###
-warn "Connecting to database\n" if $opt_v;
-
$FS::CurrentUser::upgrade_hack = 1;
$FS::UID::callback_hack = 1;
my $dbh = adminsuidsetup $opt_u; #$user;
#create tables
$|=1;
-warn "Creating tables and indices\n" if $opt_v;
-
foreach my $statement ( $dbdef->sql($dbh) ) {
$dbh->do( $statement )
or die "CREATE error: ". $dbh->errstr. "\ndoing statement: $statement";
delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
reload_dbdef($dbdef_file);
-warn "Tables and indices created - commiting transaction\n" if $opt_v;
+warn "Freeside schema initialized - commiting transaction\n" if $opt_v;
+
$dbh->commit or die $dbh->errstr;
$dbh->disconnect or die $dbh->errstr;
+
warn "Database schema committed successfully\n" if $opt_v;
-warn "Initializing configuration\n" if $opt_v;
+warn "Initializing freeside configuration\n" if $opt_v;
$FS::UID::callback_hack = 1;
$dbh = adminsuidsetup $opt_u;
$FS::UID::callback_hack = 0;
}
}
-warn "Configuration initialized - commiting transaction\n" if $opt_v;
+warn "Freeside configuration initialized - commiting transaction\n" if $opt_v;
+
$dbh->commit or die $dbh->errstr;
$dbh->disconnect or die $dbh->errstr;
-warn "Configuration committed successfully\n" if $opt_v;
+
+warn "Freeside configuration committed successfully\n" if $opt_v;
$dbh = adminsuidsetup $opt_u;
create_initial_data('domain' => $opt_d);
-warn "Database initialized - commiting transaction\n" if $opt_v;
+warn "Freeside database initialized - commiting transaction\n" if $opt_v;
+
$dbh->commit or die $dbh->errstr;
$dbh->disconnect or die $dbh->errstr;
+
warn "Database initialization committed successfully\n" if $opt_v;
sub dbdef_create { # reverse engineer the schema from the DB and save to file
}
sub usage {
- die "Usage:\n freeside-setup -d domain.name [ -q ] [ config/dir ]\n"
+ die "Usage:\n freeside-setup -d domain.name [ -v ] [ config/dir ]\n"
# [ -u user ] for devel/multi-db installs
}
if (dbdef->table('h_cust_main'));
}
-if ( dbdef->table('cgp_rule_condition') &&
- dbdef->table('cgp_rule_condition')->column('condition')
- )
-{
- push @bugfix,
- "ALTER TABLE ${_}cgp_rule_condition RENAME COLUMN condition TO conditionname"
- for '', 'h_';
-
-}
-
-if ( $DRY_RUN ) {
- print
- join(";\n", @bugfix ). ";\n";
-} elsif ( @bugfix ) {
-
- foreach my $statement ( @bugfix ) {
- warn "$statement\n";
- $dbh->do( $statement )
- or die "Error: ". $dbh->errstr. "\n executing: $statement";
- }
-
- dbdef_create($dbh, $dbdef_file);
- delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
- reload_dbdef($dbdef_file);
-
-}
-
#you should have run fs-migrate-part_svc ages ago, when you upgraded
#from 1.3 to 1.4... if not, it needs to be hooked into -upgrade here or
#you'll lose all the part_svc settings it migrates to part_svc_column
if ( $DRY_RUN ) {
print
- join(";\n", @statements ). ";\n";
+ join(";\n", @bugfix, @statements ). ";\n";
exit;
} else {
- foreach my $statement ( @statements ) {
+ foreach my $statement ( @bugfix, @statements ) {
warn "$statement\n";
$dbh->do( $statement )
or die "Error: ". $dbh->errstr. "\n executing: $statement";
$dbh->commit or die $dbh->errstr;
$dbh->disconnect or die $dbh->errstr;
-$FS::UID::AutoCommit = 1;
-
$dbh = adminsuidsetup($user);
warn "Re-initialization with updated schema completed in ". (time-$start). " seconds\n"; # if $DEBUG;
+++ /dev/null
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::cust_tag;
-$loaded=1;
-print "ok 1\n";
+++ /dev/null
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::msg_template;
-$loaded=1;
-print "ok 1\n";
+++ /dev/null
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::part_tag;
-$loaded=1;
-print "ok 1\n";
+++ /dev/null
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::rate_time;
-$loaded=1;
-print "ok 1\n";
+++ /dev/null
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::rate_time_interval;
-$loaded=1;
-print "ok 1\n";
FREESIDE_PATH = `pwd`
PERL_INC_DEV_KLUDGE = /usr/local/share/perl/5.10.1/
-VERSION=2.1.1cvs
-TAG=freeside_2_1_1
+VERSION=2.1.0
+TAG=freeside_2_1_0
DEBVERSION = `echo ${VERSION} | perl -pe 's/(\d)([a-z])/\1~\2/'`-1
+++ /dev/null
-#!/usr/bin/perl -w
-
-use strict;
-use FS::UID qw(adminsuidsetup dbh);
-use FS::Schema;
-use FS::Record qw(qsearch qsearchs);
-
-my $DRY_RUN = 1;
-$FS::UID::AutoCommit = 0;
-
-my ($user, $from_usernum, $to_usernum, $go) = @ARGV;
-die usage() if not ($user and $from_usernum and $to_usernum);
-$DRY_RUN = 0 if defined($go) and $go eq 'go';
-
-my $dbh = adminsuidsetup($user);
-
-# Sanity checks.
-die "Can't merge a user to itself." if $from_usernum == $to_usernum;
-my $from_user = FS::access_user->by_key($from_usernum) or
- die "Usernum '$from_usernum' not found.\n";
-my $to_user = FS::access_user->by_key($to_usernum) or
- die "Usernum '$to_usernum' not found.\n";
-
-my $tables = FS::Schema::tables_hashref;
-foreach my $table (keys %$tables) {
- if( grep /^usernum$/, FS::Record::real_fields($table) ) {
- next if $table eq 'access_user';
- foreach ($table, "h_$table") {
- print "$_: ";
- my $sql;
- if( $table =~ /^access_(.*)$/ ) {
- print "deleting ";
- $sql = "DELETE FROM $_ WHERE usernum = $from_usernum";
- }
- else {
- print "updating ";
- $sql = "UPDATE $_ SET usernum = $to_usernum WHERE usernum = $from_usernum";
- }
- #print $sql;
- my $sth = $dbh->prepare($sql);
- $sth->execute;
- if($dbh->err) {
- print $dbh->errstr."\n";
- $dbh->rollback;
- exit(1);
- }
- print $sth->rows, "\n";
- }
- }
-}
-
-if($DRY_RUN) {
- warn "Dry run complete. Reverting all changes.\n";
- $dbh->rollback;
-}
-else {
-# Warning: access_user->delete does not transactionize because of
-# htpasswd issues.
- print "Deleting merged user.\n";
- my $error = $from_user->delete;
- die $error if $error;
-
- warn "Committing changes.\n";
- $dbh->commit;
-}
-exit(0);
-
-sub usage {
- "Usage:\n merge-user admin_user from_usernum to_usernum [ 'go' ]\n
- (Specify 'go' to actually apply changes.)\n\n";
-}
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-use Date::Parse;
-use Date::Format;
-use FS::UID qw(adminsuidsetup);
-use FS::Record;
-
-my @date_fields = (
- 'Circuit Ordered Date',
- 'Circuit Due Date (s)',
- 'Install Date',
- 'Site Audit Date',
- 'LOCAL PORT COMPLETE',
- 'TF PORTING COMPLETE',
- '411 Submission',
- 'Billed in Freeside',
- 'Billed in Quickbooks',
-);
-#@date_fields = ( 'Custom thingie' );
-
-my $dbh = adminsuidsetup(shift) or die "Usage: rt-update-customfield-dates username\n";
-
-foreach my $date_field ( @date_fields ) {
-
- my $cf_sql = 'SELECT id FROM CustomFields where name = '. $dbh->quote($date_field);
- my $cf_sth = $dbh->prepare($cf_sql) or die $dbh->errstr;
- $cf_sth->execute or die $cf_sth->errstr;
- my $result = $cf_sth->fetchrow_arrayref
- or do { warn "$date_field not found; skipping\n"; next };
- my $customfield_id = $result->[0];
-
- my $ocfv_sql = "SELECT id, content FROM ObjectCustomFieldValues WHERE customfield = $customfield_id and content !~ '^[0-9]+\$' ";
- my $ocfv_sth = $dbh->prepare($ocfv_sql) or die $dbh->errstr;
- $ocfv_sth->execute or die $ocfv_sth->errstr;
-
- while (my $row = $ocfv_sth->fetchrow_arrayref) {
-
- my($id, $content) = @$row;
-
- my $origcontent = $content;
-
- #April 21 KW / April 21 Mont
- $content =~ s/^April (\d\d) [a-zA-Z]+$/April $1/;
-
- #SAL April 29 / other May 3
- $content =~ s/^[a-zA-Z]+ (April|May) (\d\d?)$/$1 $2/;
-
- #things like "July 8/2010 and "JUNE 24/10" are not doing what we want
- $content =~ s/^(June|July) (\d\d?)\/(20)?10$/$1 $2, 2010/i;
-
- #28/04/2010
- $content =~ s{^(2\d|1[3-9])/(0\d)/2010$}{$2/$1/2010};
-
- my $unixdate = str2time($content); #current timezone is what we want here
-
- #things like "DONE"/"ORDERED" are returning a 0 here.. should stay blank
- my $prettynew = $unixdate ? time2str('%Y-%m-%d %T', $unixdate, 'GMT') : '';
-
- print "$id: $origcontent -> $prettynew \n" unless $content =~ qr(^0\d/\d\d/2010$);
-
- my $update_sql =
- "UPDATE ObjectCustomFieldValues SET content = '$prettynew'".
- " WHERE id = $id";
-
- my $update_sth = $dbh->prepare($update_sql) or die $dbh->errstr;
- $update_sth->execute or die $update_sth->errstr;
- $dbh->commit or die $dbh->errstr;
-
- }
-
-}
<%= $summary %>
<%=
my $notfirst = 0;
- my $columncount = $unitprices ? 5 : 3;
foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) {
if ($section->{'pretotal'} && !$summary) {
$OUT .= '</table>' if $notfirst;
'<tr>';
if ($section->{header_generator}) {
- my $header = &{$section->{header_generator}}();
- $OUT .= $header;
- $columncount = scalar(my @array = split /<\/th><th/i, $header);
+ $OUT .= &{$section->{header_generator}}();
} else {
$OUT .= '<th align="center">Ref</th>'.
'<th align="left">Description</th>'.
$OUT .= '</tr>';
$lastref = $line->{'ref'};
if ( @{$line->{'ext_description'} } ) {
- unless ( $section->{description_generator} ) {
- $OUT .= '<tr class="invoice_extdesc"><td></td><td';
- $OUT .= $unitprices ? ' colspan=3' : '';
- $OUT .= '><table width="100%">';
- }
+ $OUT .= '<tr class="invoice_extdesc"><td></td><td';
+ $OUT .= $unitprices ? ' colspan=3>' : '>';
+ $OUT .= '<table width="100%">';
foreach my $ext_desc ( @{$line->{'ext_description'} } ) {
$OUT .=
'<tr class="invoice_extdesc">'.
- ( $section->{'description_generator'} ? '<td></td>' : '' ).
'<td align="left" '.
( $ext_desc =~ /<\/?TD>/i ? '' : 'colspan=99' ). '>'.
' '. $ext_desc.
'</td>'.
'</tr>'
}
- unless ( $section->{description_generator} ) {
- $OUT .= '</table></td><td></td>';
- }
- $OUT .= '</tr>';
+ $OUT .= '</table></td><td></td></tr>';
}
}
}
}
if ($section->{'posttotal'}) {
- $OUT .= '<tr><td align="right" colspan='. $columncount. '>';
+ $OUT .= '<tr><td align="right" colspan=5>';
$OUT .=
'<p><font size="+1">'. $section->{'posttotal'}.
'</font>'.
$OUT .= &{$section->{total_line_generator}}($line);
} else {
$OUT .= qq(<td style="$style"> </td>).
- qq(<td align="left" style="$style" colspan=").
- ( $columncount - 2 ). '">'.
+ qq(<td align="left" style="$style").
+ ( $unitprices ? ' colspan=3>' : '>' ).
$line->{'total_item'}. '</td>'.
qq(<td align="right" style="$style">).
$line->{'total_amount'}. '</td>';
\r
\addtolength{\voffset}{-0.0cm} % top margin to top of header\r
\addtolength{\hoffset}{-0.6cm} % left margin on page\r
-\addtolength{\topmargin}{[@-- defined($topmargin) ? $topmargin : '-1.25cm' --@]}\r
+\addtolength{\topmargin}{-1.25cm} % top margin to top of header\r
\setlength{\headheight}{2.0cm} % height of header\r
-\setlength{\headsep}{[@-- defined($headsep) ? $headsep : '1.0cm' --@]}\r
+\setlength{\headsep}{1.0cm} % between header and text\r
\setlength{\footskip}{1.0cm} % bottom of footer from bottom of text\r
\r
%\addtolength{\textwidth}{2.1in} % width of text\r
\setlength{\textwidth}{19.5cm}\r
-\setlength{\textheight}{[@-- defined($textheight) ? $textheight : '19.5cm' --@]}\r
+\setlength{\textheight}{19.5cm}\r
\setlength{\oddsidemargin}{-0.9cm} % odd page left margin\r
\setlength{\evensidemargin}{-0.9cm} % even page left margin\r
\r
}\r
}\r
\r
-\newcommand{\extracouponspace}{[@-- defined($extracouponspace) ? $extracouponspace : '3.6cm' --@]}\r
+\newcommand{\extracouponspace}{3.6cm}\r
\r
% Adjust the inset of the mailing address\r
\newcommand{\addressinset}[1][]{\hspace{1.0cm}}\r
$OUT .= '\vspace{-\extracouponspace}';\r
$OUT .= '\rule[0.5em]{\textwidth}{\footrulewidth}\\\\';\r
$OUT .= $coupon;\r
- $OUT .= '\vspace{'. $couponfootsep. '}' if defined($couponfootsep);\r
}\r
'';\r
--@] [@-- $smallerfooter ? '\scriptsize{' : '\small{' --@]\r
\returninset\r
\makebox{\r
\begin{tabular}{ll}\r
- \includegraphics{[@-- $logo_file --@]} & [@-- $verticalreturnaddress ? '\\\\' : '' --@]\r
+ \includegraphics{[@-- $logo_file --@]} &\r
\begin{minipage}[b]{5.5cm}\r
[@-- $returnaddress --@]\r
- \end{minipage}\\\r
+ \end{minipage}\r
\end{tabular}\r
}\r
}\r
\addressinset \rule{0.5cm}{0cm} \r
\makebox{\r
\begin{minipage}[t]{7.0cm}\r
-\vspace{[@-- defined($addresssep) ? $addresssep : '0.25cm' --@]}\r
+\vspace{0.25cm}\r
\textbf{[@-- $payname --@]}\\\r
\addressline{[@-- $company --@]}\r
\addressline{[@-- $address1 --@]}\r
\begin{tabular}{ll}\r
\returninset\r
\begin{tabular}{ll}\r
- \makebox{ \includegraphics{[@-- $logo_file --@]}} & [@-- $verticalreturnaddress ? '\\\\' : '' --@]\r
+ \makebox{ \includegraphics{[@-- $logo_file --@]}} &\r
\begin{minipage}[b]{5.5cm}\r
[@-- $returnaddress --@]\r
- \end{minipage}\\\r
+ \end{minipage}\r
\end{tabular}&\r
\begin{tabular}{r@{: }lr}\r
-Invoice date & \textbf{[@-- $date --@]} & \multirow{4}*{[@-- $verticalreturnaddress ? '\\rule{1.5cm}{0cm}' : '' --@]\r
+Invoice date & \textbf{[@-- $date --@]} & \multirow{4}*{\r
\makebox{\r
\begin{minipage}[t]{7.0cm}\r
\textbf{[@-- $payname --@]}\\\r
\end{minipage}}}\\\r
Customer\#& \textbf{[@-- $custnum --@]} & \\\r
Total Due & \textbf{[@-- $balance --@]} & \\\r
-\rule{0pt}{[@-- defined($amountenclosedsep) ? $amountenclosedsep : '2.25em' --@]}Amount Enclosed & \rule{2cm}{1pt}& \\\r
+\rule{0pt}{2.25em}Amount Enclosed & \rule{2cm}{1pt}& \\\r
\end{tabular}\\\r
-\rule{0pt}{[@-- defined($coupontoaddresssep) ? $coupontoaddresssep : '1cm' --@]} &\\\r
+\rule{0pt}{1cm} &\\\r
\end{tabular}\\\r
\begin{tabular}{ll}\r
\addressinset \rule{0.5cm}{0cm} &\r
\makebox{\r
\begin{minipage}[t]{7.0cm}\r
-[@-- $addcompanytoaddress ? $company_name. '\\\\' : '' --@][@-- $returnaddress --@]\r
+[@-- $returnaddress --@]\r
\end{minipage}}\r
\hfill\r
\end{tabular}\\\r
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use Frontier::Client;
-use Data::Dumper;
-
-my $phonenum = shift @ARGV;
-
-my $server = new Frontier::Client (
- url => 'http://localhost:8080/selfservice/xmlrpc.cgi',
-);
-
-my $result = $server->call('FS.ClientAPI_XMLRPC.phonenum_balance',
- 'phonenum' => $server->string($phonenum), # '3615588197',
-);
-
-#print Dumper($result);
-die $result->{'error'} if $result->{'error'};
-
-warn Dumper($result);
-
-1;
PerlHandler HTML::Mason
</Directory>
-<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/Ticket/Graphs>
-SetHandler perl-script
-PerlHandler HTML::Mason
-</Directory>
-
<Directory %%%FREESIDE_DOCUMENT_ROOT%%%/rt/Search>
SetHandler perl-script
PerlHandler HTML::Mason
#agent virt so you can't do cross-agent communigate rules
my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svcnum })
or die 'unknown svcnum';
-my $part_svc = $cust_svc->part_svc;
my $count_query = "SELECT COUNT(*) FROM cgp_rule WHERE svcnum = $svcnum";
my($svc_label, $svc_value, $svcdb) = $cust_svc->label;
+
my $view = FS::UI::Web::svc_url( 'm' => $m,
'action' => 'view',
- 'part_svc' => $part_svc,
+ 'part_svc' => $cust_svc->part_svc,
'svc' => $cust_svc,
);
my $html_init =
qq(<A HREF="$view">View this $svc_label</A><BR><BR>).
- qq!<A HREF="${p}edit/cgp_rule.html?svcnum=$svcnum">Add new rule</A><BR>!;
-
-if ( $part_svc->svcdb eq 'svc_domain' ) {
-
- #areyousure for adding these?
-
- foreach my $line ( FS::Conf->new->config('cgp_rule-domain_templates') ) {
- $line =~ /^\s*(\d+)\s+(.+)\s*$/ or next;
- my($t_svcnum, $t_name) = ( $1, $2 );
- $html_init .=
- qq!<A HREF="${p}misc/clone-cgp_rule.html?clone=$t_svcnum;svcnum=$svcnum">!
- ."Add $t_name rule</A><BR>";
- }
-
-}
-
-$html_init .=
- '<BR>'.
+ qq!<A HREF="${p}edit/cgp_rule.html?svcnum=$svcnum">Add new rule</A><BR><BR>!.
qq!
<SCRIPT>
- function areyousure_delete(href) {
- areyousure(href,"Are you sure you want to delete this rule?");
- }
- function areyousure(href,message) {
- if (confirm(message) == true)
+ function areyousure(href) {
+ if (confirm("Are you sure you want to delete this rule?") == true)
window.location.href = href;
}
</SCRIPT>
[ map {
[
- { data => $_->conditionname,
+ { data => $_->condition,
#align =>
},
{ data => $_->op,
my $edit_sub = [ $p.'edit/cgp_rule.html?', 'rulenum' ];
my $del_sub = sub {
my $rulenum = shift->rulenum;
- [ "javascript:areyousure_delete('${p}misc/delete-cgp_rule.html?$rulenum')", '' ];
+ [ "javascript:areyousure('${p}misc/delete-cgp_rule.html?$rulenum')", '' ];
};
</%init>
+++ /dev/null
-<% include( 'elements/browse.html',
- 'title' => 'Message templates',
- 'name_singular' => 'template',
- 'menubar' => [ 'Add a new template' =>
- $p.'edit/msg_template.html',
- ],
- 'query' => { 'table' => 'msg_template', },
- 'count_query' => 'SELECT COUNT(*) FROM msg_template',
- 'disableable' => 1,
- 'disabled_statuspos' => 2,
- 'agent_virt' => 1,
- 'agent_null_right' => 'Edit global templates',
- 'agent_pos' => 3,
- 'header' => [ 'Name' ],
- 'fields' => [ 'msgname' ],
- 'links' => [ $link ],
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my $link = [ "${p}edit/msg_template.html?", 'msgnum' ];
-
-</%init>
my $count_query = 'SELECT COUNT(*) FROM part_event WHERE '.
$FS::CurrentUser::CurrentUser->agentnums_sql(
- 'null_right' => 'Edit global billing events',
- 'viewall_right' => 'None',
+ 'null_right' => 'Edit global billing events',
);
my $join_conditions = FS::part_event_condition->join_conditions_sql;
join( 'AND ', @where)
: '';
-my $agentnums_sql = $curuser->agentnums_sql( 'table'=>'cust_main' );
+my $agentnums = join(',', $curuser->agentnums);
my $count_cust_pkg = "
SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum )
WHERE cust_pkg.pkgpart = part_pkg.pkgpart
- AND $agentnums_sql
+ AND cust_main.agentnum IN ($agentnums)
";
$select = "
+++ /dev/null
-<% include( 'elements/browse.html',
- 'title' => 'Tags',
- 'name_singular' => 'tag',
- 'menubar' => [ 'Add a new tag' =>
- $p.'edit/part_tag.html',
- ],
- 'query' => { 'table' => 'part_tag', },
- 'count_query' => 'SELECT COUNT(*) FROM part_tag',
- 'header' => [ 'Tag', 'Message', ],
- 'fields' => [ 'tagname', 'tagdesc', ],
- 'links' => [ $link, '', ],
- 'cell_style' => [ '', $tagdesc_style ],
- 'disableable' => 1,
- 'disabled_statuspos' => 1,
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my $tagdesc_style = sub { 'background-color:#'.shift->tagcolor };
-
-my $link = [ "${p}edit/part_tag.html?", 'tagnum' ];
-
-</%init>
'title' => 'Rate plans',
'menubar' => [ 'Regions and Prefixes' =>
$p.'browse/rate_region.html',
- 'Time Periods' =>
- $p.'browse/rate_time.html',
],
'html_init' => $html_init,
'name' => 'rate plans',
-<% include('/elements/init_overlib.html') %>
-<% include('/elements/header.html',$title) %>
-<% include('/elements/menubar.html',@menubar) %>
-<% include('/edit/elements/rate_detail.html',
- 'ratenum' => $ratenum,
- 'countrycode' => $countrycode,
-) %>
-<% include('/elements/footer.html') %>
-
+<% include( 'elements/browse.html',
+ 'title' => $title,
+ 'name_singular' => 'rate',
+ 'html_init' => $html_init,
+ 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ],
+ 'query' => {
+ 'table' => 'rate_detail',
+ 'addl_from' => $join,
+ 'hashref' => { 'ratenum' => $ratenum },
+ 'extra_sql' => $where,
+ },
+ 'count_query' => "SELECT COUNT(*) FROM rate_detail $join".
+ " WHERE ratenum = $ratenum $where",
+ 'header' => [
+ 'Region',
+ 'Prefix(es)',
+ 'Included<BR>minutes',
+ 'Connection<BR>charge',
+ 'Charge per<BR>minute',
+ 'Granularity',
+ 'Usage class',
+ ],
+ 'fields' => [
+ 'regionname',
+ sub { shift->dest_region->prefixes_short },
+ sub { shift->min_included. $edit_hint },
+ $conn_charge_sub,
+ sub { $money_char. shift->min_charge. $edit_hint },
+ sub { $granularity{ shift->sec_granularity } },
+ 'classname',
+ ],
+ 'links' => [ '', '', $edit_link, $edit_link, '', '' ],
+ 'link_onclicks' => [ '', '', $edit_onclick, $edit_onclick, '', '' ],
+ 'align' => 'llrrcc',
+ )
+%>
<%once>
+tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
+tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs();
+
my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
-my @menubar = ( 'Rate plans' => $p.'browse/rate.cgi',
- 'Regions and Prefixes' => $p.'browse/rate_region.html',
- 'Time Periods' => $p.'browse/rate_time.html',
- );
+my $join =
+ ' JOIN rate_region ON ( rate_detail.dest_regionnum = rate_region.regionnum )';
+
+my $edit_link = [ 'javascript:void(0);', sub { ''; } ];
+
+my $edit_onclick = sub {
+ my $rate_detail = shift;
+ my $ratedetailnum = $rate_detail->ratedetailnum;
+ include( '/elements/popup_link_onclick.html',
+ 'action' => "${p}edit/rate_detail.html?$ratedetailnum",
+ 'actionlabel' => 'Edit rate',
+ 'height' => 420,
+ #default# 'width' => 540,
+ #default# 'color' => '#333399',
+ );
+};
+my $edit_hint = ' <FONT SIZE="-1">(edit)</FONT>';
+
+my $conn_charge_sub = sub {
+ my $rate_detail = shift;
+ #return '' unless $rate_detail->conn_charge > 0 || $rate_detail->conn_sec;
+ $money_char. $rate_detail->conn_charge.
+ ($rate_detail->conn_sec ? ' for '.$conn_secs{$rate_detail->conn_sec} : '').
+ $edit_hint;
+};
</%once>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+my $html_init = include('/elements/init_overlib.html');
+
$cgi->param('ratenum') =~ /^(\d+)$/ or die "unparsable ratenum";
my $ratenum = $1;
my $rate = qsearchs('rate', { 'ratenum' => $ratenum } )
my $ratename = $rate->ratename;
my $title = "$ratename rates";
-my $where;
-my $countrycode = '';
+my @where = ();
+
if ( $cgi->param('countrycode') =~ /^(\d+)$/ ) {
- $countrycode = $1;
+ my $countrycode = $1;
+ push @where, "0 < ( SELECT COUNT(*) FROM rate_prefix
+ WHERE rate_prefix.regionnum = rate_region.regionnum
+ AND countrycode = '$countrycode'
+ )
+ ";
$title .= " for +$countrycode";
}
+my $where = scalar(@where) ? ' AND '.join(' AND ', @where ) : '';
+
</%init>
+++ /dev/null
-<% include( 'elements/browse.html',
- 'title' => 'Rating Time Periods',
- 'name_singular' => 'period',
- 'menubar' => [ 'Rate plans' => $p.'browse/rate.cgi' ],
- 'html_init' => $html_init,
- 'query' => {
- 'table' => 'rate_time',
- 'order_by' => 'ratetimenum', # lacking anything else
- 'hashref' => {},
- },
- 'count_query' => 'SELECT COUNT(*) FROM rate_time',
- 'header' => \@header,
- 'fields' => \@fields,
- 'links' => \@links,
- 'align' => \@align,
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my $edit_url = $p.'edit/rate_time.cgi';
-
-my $link = [ "$edit_url?", 'ratetimenum' ];
-
-my $html_init =
- 'Time periods for VoIP and call billing.<BR><BR>'.
- qq(<A HREF="$edit_url"><I>Add a new period</I></A><BR><BR>);
-
-sub interval {
- my $i = shift;
- '<TABLE>'
- .join('', map { '<TR><TD>'.($_->description)[$i].'</TR></TD>' }
- shift->intervals
- ) . '</TABLE>';
-}
-
-# inefficient but readable
-my $stime_sub = sub { interval(0,shift) };
-my $etime_sub = sub { interval(1,shift) };
-
-my @header = ( '#', 'Period', 'Start', 'End' );
-my @fields = ( 'ratetimenum', 'ratetimename', $stime_sub, $etime_sub );
-my @links = ( ($link) x 2 );
-my @align = ( 'right', 'left', 'left' );
-
-</%init>
<%init>
-
-my $curuser = $FS::CurrentUser::CurrentUser;
-die "access denied\n" unless $curuser->access_right('Configuration');
-
-my $fsconf = new FS::Conf;
-if ( $fsconf->exists('disable_settings_changes') ) {
- my @changers = split(/\s*,\s*/, $fsconf->config('disable_settings_changes'));
- my %changers = map { $_=>1 } @changers;
- unless ( $changers{$curuser->username} ) {
- errorpage("Disabled in web demo");
- die "shouldn't be reached";
- }
-}
+die "access denied\n"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
$cgi->param('confnum') =~ /^(\d+)$/ or die "illegal or missing confnum";
my $confnum = $1;
-%if ( scalar(@error) ) {
-%
-% my $url = popurl(1)."config.cgi";
-% if ( length($cgi->query_string) > 1920 ) { #stupid IE 2083 URL limit
-%
-% my $session = int(rand(4294967296)); #XXX
-% my $pref = new FS::access_user_pref({
-% 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
-% 'prefname' => "redirect$session",
-% 'prefvalue' => $cgi->query_string,
-% 'expiration' => time + 3600, #1h? 1m?
-% });
-% my $pref_error = $pref->insert;
-% if ( $pref_error ) {
-% die "FATAL: couldn't even set redirect cookie: $pref_error".
-% " attempting to set redirect$session to ". $cgi->query_string."\n";
-% }
-%
-<% $cgi->redirect("$url?redirect=$session") %>
-%
-% } else {
-%
-<% $cgi->redirect("$url?". $cgi->query_string ) %>
-%
-% }
-%
-%} else {
<% header('Configuration set') %>
<SCRIPT TYPE="text/javascript">
% my $n = 0;
% my $record = qsearchs($table, { $pkey => $key });
% my $value = $record ? "$key: ".$record->$namecol() : $key;
configCell.innerHTML = <% $value |js_string %>;
-% } elsif ( $type eq 'select-sub' && ! $i->multiple ) {
+% } elsif ( $type eq 'select-sub' ) {
configCell.innerHTML =
<% $conf->config($i->key, $agentnum) |js_string %> + ': ' +
<% &{ $i->option_sub }( $conf->config($i->key, $agentnum) ) |js_string %>;
</SCRIPT>
</BODY>
</HTML>
-%}
<%once>
#false laziness w/config-view.cgi
my %namecol = (
);
</%once>
<%init>
-
-my $curuser = $FS::CurrentUser::CurrentUser;
-die "access denied\n" unless $curuser->access_right('Configuration');
+die "access denied\n"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
my $conf = new FS::Conf;
-
-if ( $conf->exists('disable_settings_changes') ) {
- my @changers = split(/\s*,\s*/, $conf->config('disable_settings_changes'));
- my %changers = map { $_=>1 } @changers;
- unless ( $changers{$curuser->username} ) {
- errorpage_popup("Disabled in web demo");
- die "shouldn't be reached";
- }
-}
-
$FS::Conf::DEBUG = 1;
my @config_items = grep { $_->key != ~/^invoice_(html|latex|template)/ }
$conf->config_items;
my $key = $cgi->param('key');
my $i = $confitems{$key};
-my @error = ();
my @touch = ();
my @delete = ();
my $n = 0;
if ( $cgi->param($i->key.$n) ne '' ) {
my $value = $cgi->param($i->key.$n);
$value =~ s/\r\n/\n/g; #browsers?
- my $error = &{$i->validate}($value, $n) if $i->validate;
- push @error, $error if $error;
$conf->set($i->key, $value, $agentnum);
} else {
$conf->delete($i->key, $agentnum);
} elsif ( $type eq 'binary' || $type eq 'image' ) {
if ( defined($cgi->param($i->key.$n)) && $cgi->param($i->key.$n) ) {
my $fh = $cgi->upload($i->key.$n);
- my $error = &{$i->validate}($fh, $n) if $i->validate;
- push @error, $error if $error;
if (defined($fh)) {
local $/;
$conf->set_binary($i->key, <$fh>, $agentnum);
|| $i->multiple )
) {
if ( scalar(@{[ $cgi->param($i->key.$n) ]}) ) {
- my $error = &{$i->validate}([ $cgi->param($i->key.$n) ], $n) if $i->validate;
- push @error, $error if $error;
$conf->set($i->key, join("\n", @{[ $cgi->param($i->key.$n) ]} ), $agentnum);
} else {
$conf->delete($i->key, $agentnum);
}
} elsif ( $type =~ /^(text|select(-(sub|part_svc|part_pkg|pkg_class))?)$/ ) {
if ( $cgi->param($i->key.$n) ne '' ) {
- my $error = &{$i->validate}($cgi->param($i->key.$n), $n) if $i->validate;
- push @error, $error if $error;
$conf->set($i->key, $cgi->param($i->key.$n), $agentnum);
} else {
$conf->delete($i->key, $agentnum);
$conf->touch($_, $agentnum) foreach @touch;
$conf->delete($_, $agentnum) foreach @delete;
-if (scalar(@error)) {
- $cgi->param('error', join(' ', @error));
-}
-
</%init>
my @deleteable = qw( invoice_latexreturnaddress invoice_htmlreturnaddress );
my %deleteable = map { $_ => 1 } @deleteable;
-my @sections = qw(required billing invoicing notification UI self-service username password session shell BIND );
+my @sections = qw(required billing invoicing UI self-service username password session shell BIND );
push @sections, '', 'deprecated';
my %section_items = ();
% } elsif ( $type eq 'select-sub' ) {
- <select name="<% "$key$n" %>" <% $config_item->multiple ? 'MULTIPLE' : '' %>>
-
-% unless ( $config_item->multiple ) {
- <option value="">
-% }
+ <select name="<% "$key$n" %>"><option value="">
% my %options = &{$config_item->options_sub};
% my @options = sort { $a <=> $b } keys %options;
% foreach my $value ( @options ) {
% local($^W)=0; next if $saw{$value}++;
- <option value="<% $value %>"
-
-% if ( $value eq $conf->config($key, $agentnum)
-% || ( $config_item->multiple
-% && grep { $_ eq $value } $conf->config($key, $agentnum) ) ){
-
- SELECTED
-
-% }
-
- ><% $value %>: <% $options{$value} %>
+ <option value="<% $value %>" <% $value eq $conf->config($key, $agentnum) ? 'SELECTED' : '' %>><% $value %>: <% $options{$value} %>
% }
% my $curvalue = $conf->config($key, $agentnum);
<BR>
<CENTER>
-<FONT SIZE="-3">"Brown eyed women and red grenadine / the bottle was dusty but the liquor was clean" -R. Hunter</FONT>
+<FONT SIZE="-3">"Once in a while you get shown the light, in the strangest of places if you look at it right" -R. Hunter</FONT>
</CENTER>
<SCRIPT TYPE="text/javascript">
<CENTER>
<H3>Core Team</H3>
+Peter Bowen<BR>
Jeremy Davis<BR>
Jeff Finucane<BR>
Jason Hall<BR>
<BR>
<H3>Core Emeritus</H3>
-Peter Bowen<BR>
Kristian Hoffman<BR>
Brian McCane<BR>
Matt Simerson<BR>
+++ /dev/null
-<% include('/elements/header-popup.html', 'Redirect all mail') %>
-
-<FORM NAME="RedirectAllForm" ACTION="process/cgp_rule-redirect_all.html" METHOD=POST>
-
-%# XXX upstream Redirect 1
-
-<% ntable("#cccccc", 2) %>
-
-<TR>
- <TD ALIGN="right">Redirect all mail to</TD>
- <TD><textarea name="RedirectText" rows="5" cols="50"></textarea></TD>
-</TR>
-
-<% include('/elements/tr-checkbox.html',
- 'name' => 'RedirKeep',
- 'label' => 'Keep a copy',
- 'value' => 1,
- 'curr_value' => '', #XXX
- )
-%>
-
-<% include('/elements/tr-checkbox.html',
- 'name' => 'RedirHuman',
- 'label' => 'Do not redirect automatic messages',
- 'value' => 1,
- 'curr_value' => '', #XXX
- )
-%>
-
-<% include('/elements/tr-checkbox.html',
- 'name' => 'KeepToAndCc',
- 'label' => 'Preserve To/Cc fields',
- 'value' => 1,
- 'curr_value' => '', #XXX
- )
-%>
-
-</TABLE>
-
-<BR>
-<INPUT TYPE="submit" VALUE="Redirect all mail">
-%#XXX Add/Edit
-
-</FORM>
-
-</BODY>
-</HTML>
-<%init>
-
-my %opt = @_;
-
-my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } )
- or die "unknown svcnum";
-
-#XXX look for existing redirect all rule
-
-</%init>
+++ /dev/null
-<% include('/elements/header-popup.html', 'Vacation rule') %>
-
-<FORM NAME="VacationForm" ACTION="process/cgp_rule-vacation.html" METHOD=POST>
-
-%# XXX upstream Vacation 1
-
-<% ntable("#cccccc", 2) %>
-
-<TR>
- <TD ALIGN="right">Vacation message</TD>
- <TD><textarea name="VacationText" rows="5" cols="50"></textarea></TD>
-</TR>
-
-<% include('/elements/tr-input-date-field.html', {
- 'label' => 'Ends',
- 'name' => 'vacationTill',
- 'value' => '', #XXX
- })
-%>
-%# XXX upstream:
-%# VacationTill 1
-%# vacationDay
-%# vacationMonth
-%# vacationYear
-
-%#XXX Clear 'Replied Addresses' List
-
-</TABLE>
-
-<BR>
-<INPUT TYPE="submit" VALUE="Add vacation message">
-%#XXX Add/Edit
-
-</FORM>
-
-</BODY>
-</HTML>
-<%init>
-
-my %opt = @_;
-
-my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } )
- or die "unknown svcnum";
-
-#XXX look for existing vacation rule
-
-</%init>
my $m2_error_callback_cond = sub {
my($cgi, $object) = @_;
- my @fields = qw( conditionname op params );
+ my @fields = qw( condition op params );
my @gfields = ( '', map "_$_", @fields );
map {
if ( /^ruleactionnum(\d+)$/ ) {
my $num = $1;
if ( grep $cgi->param("ruleactionnum$num$_"), @gfields ) {
- my $x = new FS::cgp_rule_action {
+ my $x = new FS::cgp_rule_condition {
'ruleactionnum' => $cgi->param("ruleactionnum$num"),
map { $_ => scalar($cgi->param("ruleactionnum${num}_$_")) } @fields,
};
<% include('/elements/header.html',
- $title,
+ "Customer $action",
'',
' onUnload="myclose()"' #hmm, in billing.html
) %>
% }
-<INPUT TYPE="hidden" NAME="usernum" VALUE="<% $cust_main->usernum %>">
+<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $cust_main->otaker %>">
%# cust_main/bottomfixup.js
% foreach my $hidden (
$cgi->delete_all();
$cgi->param('error', $error);
-my $title = $custnum ? 'Edit Customer' : 'Add Customer';
-$title .= ": ". $cust_main->name if $custnum;
+my $action = $custnum ? 'Edit' : 'Add';
+$action .= ": ". $cust_main->name if $custnum;
my $r = qq!<font color="#ff0000">*</font> !;
<% ntable("#cccccc", 2) %>
- <% include( '/elements/tr-input-date-field.html',
- 'birthdate',
- $cust_main->birthdate,
- 'Date of Birth',
- ( $conf->config('date_format') || "%m/%d/%Y" ),
- 1
- )
+ <% include ('/elements/tr-input-date-field.html',
+ 'birthdate',
+ $cust_main->birthdate,
+ 'Date of Birth',
+ $conf->config('date_format') || "%m/%d/%Y",
+ 1)
%>
</TABLE>
<%init>
<% &ntable("#cccccc") %>
-%# tags
-<% include('/elements/tr-select-cust_tag.html',
- 'custnum' => $custnum,
- 'cgi' => $cgi,
- )
-%>
-
%# agent
<% include('/elements/tr-select-agent.html',
'curr_value' => $cust_main->agentnum,
'label' => "<B>${r}Agent</B>",
'empty_label' => 'Select agent',
'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ),
- 'viewall_right' => 'None', #override default 'View customers of all agents'
)
%>
% foreach grep exists($f->{$_}),
% qw( hashref agent_virt agent_null agent_null_right );
%
-% #htmlarea
-% $include_common{$_} = $f->{$_}
-% foreach grep exists($f->{$_}), qw( width height );
-%
-%
% if ( $type eq 'tablebreak-tr-title' ) {
% $include_common{'table_id'} = 'TableNumber'. $tablenum++;
% }
var field_regex = /(\d+)(_[a-z]+)?$/;
var match = field_regex.exec(what.name);
if ( !match ) {
- alert(what.name + " didn't match for " + what);
+ alert(what.name + " didn't match?!");
return;
}
if ( match[1] != <%$field%>_fieldnum ) {
+++ /dev/null
-<%doc>
-<% include('/edit/elements/rate_detail.html',
- # required
- 'ratenum' => '1',
-
- # optional
- 'regionnum' => '25',
- # or
- 'countrycode' => '237',
-) %>
-
-If regionnum is specified, this produces column headers plus
-one row of rate details for that region (in all time periods).
-Otherwise, there's one row for each region in the specified
-countrycode (or each region anywhere, if there is no countrycode),
-with row headers showing the region name and prefixes.
-
-</%doc>
-<% include('/elements/table-grid.html') %>
-<TR>
-% my $col = 0;
-% foreach (@header) {
-% my $hlink = $hlinks[$col];
- <TH CLASS = "grid",
- BGCOLOR = "#cccccc">
- <% $hlink ? qq!<A HREF="$hlink">$_</A>! : $_ %>
- </TH>
-% $col++;
-% } #foreach @header
-</TR><TR>
-% my $row = 0;
-% foreach my $r (@rows) {
-% $col = 0;
-% if ( !$opt{'regionnum'} ) {
-% $region = $r;
-% foreach ($r->regionname, $r->prefixes_short) {
- <TD>
- <A HREF="<% $p.'edit/rate_region.cgi?'.$r->regionnum %>"><% $_ %></A>
- </TD>
-% }
-% }
-% elsif ( !$opt{'ratenum'} ) {
-% $rate = $r;
- <TD>
- <A HREF="<% $p.'edit/rate.cgi?'.$r->ratenum %>"><% $r->ratename %></A>
- </TD>
-% }
-% foreach my $rate_time (@rate_time, '') {
- <TD>
-% my $detail = $details[$row][$col];
-% if($detail) {
- <TABLE CLASS="inv" STYLE="border:none">
- <TR><TD><% edit_link($detail) %><% $money_char.$detail->min_charge %>
- <% $detail->sec_granularity ? ' / minute':' / call' %>
- <% $edit_hint %></A>
- </TD></TR>
- <% granularity_detail($detail) %>
- <% min_included_detail($detail) %>
- <% conn_charge_detail($detail) %>
- <TR><TD><% $rate_time ? delete_link($detail) : '' %></TD></TR>
- </TABLE>
-% }
-% else { #!$detail
- <% add_link($rate, $region, $rate_time) %>
-% }
-% $col++;
- </TD>
-% } # foreach @rate_time
-</TR>
-% $row++;
-% }# foreach @rate_region
-</TABLE>
-
-<%once>
-
-tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities();
-tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs();
-
-my $conf = new FS::Conf;
-my $money_char = $conf->config('money_char') || '$';
-
-sub small {
- '<FONT SIZE="-1">'.shift.'</FONT>'
-}
-my $edit_hint = small('(edit)');
-
-sub edit_link {
- my $rate_detail = shift;
- my $ratedetailnum = $rate_detail->ratedetailnum;
- '<A HREF="javascript:void(0);" onclick="'.
- include( '/elements/popup_link_onclick.html',
- 'action' => "${p}edit/rate_detail.html?$ratedetailnum",
- 'actionlabel' => 'Edit rate',
- 'height' => 420,
- #default# 'width' => 540,
- #default# 'color' => '#333399',
- ) . '">'
-}
-
-sub add_link {
- my ($rate, $region, $rate_time) = @_;
- '<A HREF="javascript:void(0);" onclick="'.
- include( '/elements/popup_link_onclick.html',
- 'action' => "${p}edit/rate_detail.html?ratenum=".
- $rate->ratenum.
- ';dest_regionnum='.
- $region->regionnum.
- ($rate_time ?
- ';ratetimenum='.$rate_time->ratetimenum :
- ''),
- 'actionlabel' => 'Add rate',
- 'height' => 420,
- ).'">'.small('(add)').'</A>'
-}
-
-sub delete_link {
- my $rate_detail = shift;
- my $ratedetailnum = $rate_detail->ratedetailnum;
- my $onclick = include( '/elements/popup_link_onclick.html',
- 'action' => "${p}misc/delete-rate_detail.html?$ratedetailnum",
- 'actionlabel' => 'Delete rate',
- 'width' => 510,
- 'height' => 315,
- 'frame' => 'top',
- );
- $onclick = "if(confirm('Delete this rate?')) { $onclick }";
- qq!<A HREF="javascript:void(0);" onclick="$onclick">!.small('(delete)').'</A>'
-}
-
-sub granularity_detail {
- my $rate_detail = shift;
- if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) {
- '<TR><TD>'.
- small('in '.$granularity{$rate_detail->sec_granularity}.' increments').
- '</TD></TR>';
- }
- else { '' }
-}
-
-sub min_included_detail {
- my $rate_detail = shift;
- if($rate_detail->min_included) {
- '<TR><TD>'.
- small( $rate_detail->min_included .
- ($rate_detail->sec_granularity ?
- ' minutes included' :
- ' calls included') ).
- '</TD></TR>'
- }
- else { '' }
-}
-
-sub conn_charge_detail {
- my $rate_detail = shift;
- if($rate_detail->conn_charge > 0) {
- #return '' unless $rate_detail->conn_charge > 0 || $rate_detail->conn_sec;
- '<TR><TD>'.
- small( $money_char. $rate_detail->conn_charge.
- ' for '.$conn_secs{$rate_detail->conn_sec}
- ).
- '</TD></TR>'
- }
- else { '' }
-}
-
-</%once>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my %opt = @_;
-my $ratenum = $opt{'ratenum'} || '';
-my $regionnum = $opt{'regionnum'} || '';
-
-# either of these, if the $opt isn't passed, will be set to the
-# correct object when generating each row.
-my $rate = qsearchs('rate', { 'ratenum' => $ratenum } ) if $ratenum;
-my $region = qsearchs('rate_region', { 'regionnum' => $regionnum }) if $regionnum;
-
-my @rate_time = qsearch('rate_time', {});
-my @header = (
- map( { $_->ratetimename } @rate_time ),
- 'Default rate');
-my @hlinks = map( {$p.'edit/rate_time.cgi?'.$_->ratetimenum} @rate_time ), '';
-my @rtns = ( map( { $_->ratetimenum } @rate_time ), '' );
-
-my @details;
-my @rows;
-if ( $ratenum ) {
- if ( $regionnum ) {
- @rows = qsearch('rate_region',
- { ratenum => $ratenum, regionnum => $regionnum });
- }
- else {
- my $where = '';
- if ( $opt{'countrycode'} ) {
- $where = "WHERE 0 < (
- SELECT COUNT(*) FROM rate_prefix
- WHERE rate_prefix.regionnum = rate_region.regionnum
- AND countrycode = '$opt{countrycode}'
- )";
- }
- @rows = qsearch({ table => 'rate_region',
- hashref => { },
- extra_sql => $where,
- });
- die "no region found" if !@rows;
-
- unshift @header, 'Region', 'Prefix(es)';
- unshift @hlinks, '', '';
- }
- foreach my $region (@rows) {
- push @details, [ map { qsearchs('rate_detail',
- { 'ratenum' => $ratenum,
- 'dest_regionnum' => $region->regionnum,
- 'ratetimenum' => $_ } ) or ''
- } @rtns
- ];
- }
-}
-elsif ( $regionnum ) {
- @rows = qsearch('rate', {}) or die "no rate plans found";
- unshift @header, 'Rate plan';
- unshift @hlinks, '';
- foreach my $rate (@rows) {
- push @details, [ map { qsearchs('rate_detail',
- { 'ratenum' => $rate->ratenum,
- 'dest_regionnum' => $regionnum,
- 'ratetimenum' => $_ } ) or ''
- } @rtns
- ];
- }
-}
-else {
- die "no ratenum or regionnum specified";
-}
-
-</%init>
+++ /dev/null
-<% include( 'elements/edit.html',
- 'html_init' => '<TABLE id="outerTable"><TR><TD>',
- 'name_singular' => 'template',
- 'table' => 'msg_template',
- 'viewall_dir' => 'browse',
- 'agent_virt' => 1,
- 'agent_null' => 1,
- 'agent_null_right' => 'Edit global templates',
-
- 'fields' => [ 'msgname',
- 'subject',
- 'from_addr',
- { field=>'body', type=>'htmlarea', width=>763 },
- ],
- 'labels' => { 'msgnum' => 'Template',
- 'msgname' => 'Template name',
- 'from_addr' => 'Return address',
- 'subject' => 'Message subject',
- 'body' => 'Message template',
- },
- 'html_foot' => "</TD>$sidebar</TR></TABLE>",
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-# Create hints pane
-
-my %substitutions = (
- 'cust_main' => [
- '$display_custnum'=> 'Customer#',
- '$agentnum' => 'Agent#',
- '$agent_name' => 'Agent name',
- '$payby' => 'Payment method',
- '$paymask' => 'Card/account# (masked)',
- '$payname' => 'Name on card/bank name',
- '$paytype' => 'Account type',
- '$payip' => 'IP address used to submit payment info',
- '$num_ncancelled_pkgs' => '# of active packages',
- '$num_cancelled_pkgs' => '# of cancelled packages',
- '$num_pkgs' => '# of packages',
- '$classname' => 'Customer class',
- '$categoryname' => 'Customer category',
- '$balance' => 'Current balance',
- '$invoicing_list_emailonly' => 'Billing email address',
- '$cust_status' => 'Status',
- '$ucfirst_cust_status' => 'Status, capitalized',
- '$cust_statuscolor' => 'Status color code',
- '$company_name' => 'Our company name',
- ],
- 'contact' => [ # duplicate this for shipping
- '$name' => 'Company and contact name',
- '$name_short' => 'Company or contact name',
- '$company' => 'Company name',
- '$contact' => 'Contact name (last, first)',
- '$contact_firstlast'=> 'Contact name (first last)',
- '$first' => 'First name',
- '$last' => 'Last name',
- '$address1' => 'Address line 1',
- '$address2' => 'Address line 2',
- '$city' => 'City',
- '$county' => 'County',
- '$state' => 'State',
- '$zip' => 'Zip',
- '$country' => 'Country',
- '$daytime' => 'Day phone',
- '$night' => 'Night phone',
- '$fax' => 'Fax',
- ],
- 'cust_bill' => [
- '$invnum' => 'Invoice#',
- ],
- 'cust_pkg' => [
- '$pkgnum' => 'Package#',
- '$pkg_label' => 'Package label (short)',
- '$pkg_label_long' => 'Package label (long)',
- '$status' => 'Status',
- '$statuscolor' => 'Status color code',
- '$start_ymd' => 'Start date',
- '$setup_ymd' => 'Setup date',
- '$last_bill_ymd' => 'Last bill date',
- '$next_bill_ymd' => 'Next bill date',
- '$susp_ymd' => 'Suspended on date',
- '$cancel_ymd' => 'Canceled on date',
- '$adjourn_ymd' => 'Adjournment date',
- '$expire_ymd' => 'Expiration date',
- '$labels_short' => 'Service labels',
- '$location_label' => 'Service location',
- ],
- 'svc_acct' => [
- '$username' => 'Login name',
- '$password' => 'Password',
- ],
- 'cust_pay' => [
- '$paynum' => 'Payment#',
- '$paid' => 'Amount',
- '$payby' => 'Payment method',
- '$date' => 'Payment date',
- '$payinfo' => 'Card/account# (masked)',
- ],
-);
-my @c = @{ $substitutions{'contact'} };
-for (my $i=0; $i<scalar(@c); $i += 2) {
- $c[$i] =~ s/\$(.*)/\$ship_$1/;
-}
-$substitutions{'shipping'} = \@c;
-
-tie my %sections, 'Tie::IxHash', (
-'contact' => 'Name and contact info (billing)',
-'shipping' => 'Name and contact info (shipping)',
-'cust_main' => 'Customer status and payment info',
-'cust_pkg' => 'Package fields',
-'cust_bill' => 'Invoice fields',
-'cust_pay' => 'Payment fields',
-'svc_acct' => 'Login service fields',
-);
-
-my $widget = new HTML::Widgets::SelectLayers(
- 'options' => \%sections,
- 'form_name' => 'dummy',
- 'html_between'=>'</FORM><FONT SIZE=-1>',
- 'selected_layer'=>(keys(%sections))[0],
- 'layer_callback' => sub {
- my $section = shift;
- my $html = include('/elements/table-grid.html');
- my @hints = @{ $substitutions{$section} };
- while(@hints) {
- my $key = shift @hints;
- $html .= qq!\n<TR><TD><A href="javascript:insertHtml('{$key}')">$key</A></TD>!;
- $html .= "\n<TD>".shift(@hints).'</TD></TR>';
- }
- $html .= "\n</TABLE>";
- return $html;
- },
-);
-
-my $sidebar = '
-<SCRIPT TYPE="text/javascript">
-function insertHtml(what) {
- var oEditor = FCKeditorAPI.GetInstance("body");
- oEditor.InsertHtml(what);
-};
-</SCRIPT>
-<TD valign="top"><FORM name="dummy">
-Substitutions: '
-. $widget->html .
-'<BR>Click links to insert.
-<BR>Enclose substitutions and other Perl expressions in braces:
-<BR>{ $name } = ExampleCo (Smith, John)
-<BR>{ time2str("%D", time) } = '.time2str("%D", time).'
-</FONT></TD>
-';
-
-</%init>
$html .= ' MULTIPLE'
if $href->{$field}{'type'} eq 'select_multiple';
$html .= qq! NAME="${layer}__$field">!;
-
- $html .= '<OPTION VALUE="">'. $href->{$field}{'empty_label'}
- if exists($href->{$field}{'disable_empty'})
- && ! $href->{$field}{'disable_empty'};
if ( $href->{$field}{'select_table'} ) {
foreach my $record (
+++ /dev/null
-<% include( 'elements/edit.html',
- 'table' => 'part_tag',
- 'name_singular' => 'tag',
- 'fields' => [
- { field=>'tagname', type=>'text', size=>10 },
- { field=>'disabled', type=>'checkbox', value=>'Y' },
- { field=>'tagdesc', type=>'text', size=>60 },
- $tagcolor,
- ],
- 'labels' => { 'tagnum' => 'Tag #',
- 'tagname' => 'Tag',
- 'tagdesc' => 'Message',
- 'tagcolor' => 'Highlight Color',
- 'disabled' => 'Disabled',
- },
- 'viewall_dir' => 'browse',
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my $tagcolor = {
- field => 'tagcolor',
- type => 'pickcolor',
-};
-
-</%init>
+% if ( $conf->exists('disable_acl_changes') ) {
+ ACL changes disabled in public demo.
+% } else {
<% include( 'elements/process.html',
'table' => 'access_group',
'viewall_dir' => 'browse',
},
)
%>
+% }
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-if ( FS::Conf->new->exists('disable_acl_changes') ) {
- errorpage('ACL changes disabled in public demo.');
- die "shouldn't be reached";
-}
+my $conf = new FS::Conf;
</%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-if ( FS::Conf->new->exists('disable_acl_changes') ) {
- errorpage('ACL changes disabled in public demo.');
- die "shouldn't be reached";
-}
-
</%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-if ( FS::Conf->new->exists('disable_acl_changes') ) {
- errorpage('ACL changes disabled in public demo.');
- die "shouldn't be reached";
-}
-
</%init>
'process_o2m' => [
{
'table' => 'cgp_rule_condition',
- 'fields' => [qw( conditionname op params )],
+ 'fields' => [qw( condition op params )],
},
{
'table' => 'cgp_rule_action',
'fields' => [qw( action params )],
},
],
- 'noerror_callback' => sub {
- my( $cgi, $object ) = @_;
- my $error = $object->svc_export;
- #shit, not a good place for error handling :/
- die $error if $error;
- },
)
%>
<%init>
);
}
-$new->tagnum( [ $cgi->param('tagnum') ] );
-
my %usedatetime = ( 'birthdate' => 1 );
foreach my $dfield (qw( birthdate signupdate )) {
#return an error string or empty for no error
'precheck_callback' => sub { my( $cgi ) = @_; },
- #after everything's inserted
- 'noerror_callback' => sub { my( $cgi, $object ) = @_; },
-
#supplies arguments to insert() and replace()
# for use with tables that are FS::option_Common (among other things)
'args_callback' => sub { my( $cgi, $object ) = @_; },
die "illegal agentnum"
unless $curuser->agentnums_href->{$new->agentnum}
- or $curuser->access_right('View customers of all agents')
or $opt{'agent_null_right'}
&& ! $new->agentnum
&& $curuser->access_right($opt{'agent_null_right'});
if ( $error ) {
-
$cgi->param('error', $error);
if ( $opt{'clear_on_error'} && scalar(@{$opt{'clear_on_error'}}) ) {
foreach my $field (@{$opt{'clear_on_error'}}) {
$cgi->param($field, '')
}
}
-
-} else {
-
- if ( $opt{'noerror_callback'} ) {
- &{ $opt{'noerror_callback'} }( $cgi, $new );
- }
-
}
</%init>
+%
+%
+% my %opt = @_;
+% my $table = $opt{'table'};
+% $opt{'fields'} ||= [ fields($table) ];
+% push @{ $opt{'fields'} }, qw( pkgnum svcpart );
+%
+%
<% include( 'process.html',
'edit_ext' => 'cgi',
'redirect' => popurl(3)."view/$table.cgi?",
%opt,
)
%>
-<%init>
-my %opt = @_;
-my $table = $opt{'table'};
-$opt{'fields'} ||= [ fields($table) ];
-push @{ $opt{'fields'} }, qw( pkgnum svcpart );
-
-</%init>
+++ /dev/null
-<% include( 'elements/process.html',
- 'table' => 'msg_template',
- 'viewall_dir' => 'browse',
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-</%init>
+++ /dev/null
-<% include( 'elements/process.html',
- 'table' => 'part_tag',
- 'viewall_dir' => 'browse',
- )
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-</%init>
%if ( $error ) {
% $cgi->param('error', $error);
<% $cgi->redirect(popurl(2). "rate_region.cgi?". $cgi->query_string ) %>
-%} elsif ( $action eq 'Add' ) {
-<% $cgi->redirect(popurl(2). "rate_region.cgi?$regionnum") %>
%} else {
<% $cgi->redirect(popurl(3). "browse/rate_region.html") %>
%}
unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
my $regionnum = $cgi->param('regionnum');
-my $action = $regionnum ? 'Edit' : 'Add';
my $old = qsearchs('rate_region', { 'regionnum' => $regionnum } ) if $regionnum;
'npa' => $_,
}
} @npa;
-# we no longer process dest_detail records here
+
+my @dest_detail = map {
+ my $ratenum = $_->ratenum;
+ new FS::rate_detail {
+ 'ratenum' => $ratenum,
+ map { $_ => $cgi->param("$_$ratenum") }
+ qw( min_included conn_charge conn_sec min_charge sec_granularity classnum )
+ };
+} qsearch('rate', {} );
+
+
my $error;
if ( $regionnum ) {
- $error = $new->replace($old, 'rate_prefix' => \@rate_prefix );
+ $error = $new->replace($old, 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => \@dest_detail, );
} else {
- $error = $new->insert( 'rate_prefix' => \@rate_prefix );
+ $error = $new->insert( 'rate_prefix' => \@rate_prefix,
+ 'dest_detail' => \@dest_detail, );
$regionnum = $new->getfield('regionnum');
}
+++ /dev/null
-% if ( $error ) {
-% $cgi->param('error', $error);
-<% $cgi->redirect(popurl(2). "rate_time.cgi?". $cgi->query_string ) %>
-% } else {
-<% $cgi->redirect(popurl(3). "browse/rate_time.html" ) %>
-% }
-%# dumper_html(\%vars, \%old_ints, {$rate_time->intervals}) %>
-<%init>
-my $error = '';
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-my $ratetimenum = $cgi->param('ratetimenum');
-my $ratetimename = $cgi->param('ratetimename');
-my $delete = $cgi->param('delete');
-
-my %vars = $cgi->Vars;
-#warn Dumper(\%vars)."\n";
-
-my $rate_time;
-
-my %old_ints;
-if( $ratetimenum ) {
- # editing
- $rate_time = FS::rate_time->by_key($ratetimenum);
-
- # make a list of existing intervals that will be deleted
- foreach ($rate_time->intervals) {
- $old_ints{$_->intervalnum} = $_;
- }
-
- if ( $delete ) {
- $error = $rate_time->delete;
- # intervals will be deleted later
- }
- elsif( $ratetimename ne $rate_time->ratetimename ) {
- # the only case where the rate_time itself must be replaced
- $rate_time->ratetimename($ratetimename);
- $error = $rate_time->replace;
- }
-}
-else { #!$ratetimenum, adding new
- $rate_time = FS::rate_time->new({ ratetimename => $ratetimename });
- $error = $rate_time->insert;
- $ratetimenum = $rate_time->ratetimenum;
-}
-
-my @new_ints;
-if(!$delete and !$error) {
- foreach my $i (map { /^sd(\d+)$/ } keys(%vars)) {
- my $stime = l2wtime(@vars{"sd$i", "sh$i", "sm$i", "sa$i"});
- my $etime = l2wtime(@vars{"ed$i", "eh$i", "em$i", "ea$i"});
- #warn "$i: $stime - $etime";
- next if !defined($stime) or !defined($etime) or $etime == $stime;
- # try to avoid needlessly wiping and replacing intervals every
- # time this is edited.
- if( %old_ints ) {
- my $this_int = qsearchs('rate_time_interval',
- { ratetimenum => $ratetimenum,
- stime => $stime,
- etime => $etime, } );
- if($this_int) {
- delete $old_ints{$this_int->intervalnum};
- #warn "not deleting $stime-$etime\n";
- next; #$i
- }
- }
- push @new_ints, FS::rate_time_interval->new({ ratetimenum => $ratetimenum,
- stime => $stime,
- etime => $etime, } );
- }
-}
-if(!$error) {
- foreach (values(%old_ints)) {
- $error = $_->delete;
- #warn "deleting ".$_->stime.' '.$_->etime."\n";
- last if $error;
- }
-}
-if(!$error) {
- # do this last to avoid overlap errors with deleted intervals
- foreach (@new_ints) {
- $error = $_->insert;
- #warn "inserting $stime-$etime\n";
- last if $error;
- }
-}
-
-sub l2wtime {
- my ($d, $h, $m, $a) = @_;
- $h += 24*$d + 12*$a;
- $m += 60*$h;
- return 60*$m
-}
-</%init>
grep { $new->$_ }
qw( seconds upbytes downbytes totalbytes );
- $error ||= "invalid $_" foreach grep { $hash{$_} !~ /^\d+$/ } keys %hash;
$error ||= $new->set_usage(\%hash); #unoverlimit and trigger radius changes
last; #once is enough
}
'contactnum' => $cgi->param("contactnum$num"),
map { $_ => scalar($cgi->param("contactnum${num}_$_")) } @fields,
};
+ use Data::Dumper; warn Dumper $x;
$x;
} else {
();
$cgi->param;
};
-#my @agentnums = $FS::CurrentUser::CurrentUser->agentnums;
+my @agentnums = $FS::CurrentUser::CurrentUser->agentnums;
</%init>
<INPUT NAME="submit" TYPE="button" VALUE="<%
$rate->ratenum ? "Apply changes" : "Add rate plan"
%>" onClick="document.OneTrueForm.submit.disabled=true; process();">
-</FORM>
-% if($rate->ratenum) {
-<BR><BR><FONT SIZE="+2">Rates in this plan</FONT>
-<% include('/edit/elements/rate_detail.html',
- 'ratenum' => $rate->ratenum
-) %>
-% }
+</FORM>
<% include('/elements/footer.html') %>
'labels' => { 'ratedetailnum' => 'Rate', #should hide...
'dest_regionname' => 'Region',
'dest_prefixes_short' => 'Prefix(es)',
- 'rate_time_name' => 'Time period',
'min_included' => 'Included minutes/calls',
'conn_charge' => 'Connection charge',
'conn_sec' => 'For',
{ field=>'ratenum', type=>'hidden', },
{ field=>'orig_regionnum', type=>'hidden', },
{ field=>'dest_regionnum', type=>'hidden', },
- { field=>'ratetimenum', type=>'hidden', },
{ field=>'dest_regionname', type=>'fixed', },
{ field=>'dest_prefixes_short', type=>'fixed', },
- { field=>'rate_time_name', type=>'fixed', },
{ field=>'min_included', type=>'text', size=>5 },
{ field=>'conn_charge', type=>'money', size=>4 },
{ field =>'conn_sec',
},
],
- 'new_hashref_callback' => sub {
- { ratenum => $cgi->param('ratenum'),
- dest_regionnum => $cgi->param('dest_regionnum'),
- ratetimenum => $cgi->param('ratetimenum'),
- }
- },
)
%>
<%once>
</TABLE>
-<BR><BR>
-<INPUT TYPE="submit" VALUE="<% $rate_region->regionnum ? "Apply changes" : "Add region" %>">
-</FORM>
-%# rate plan info, if the region has been created yet
+%# rate plan info
-% if($rate_region->regionnum) {
<BR>
-<BR>
-<FONT SIZE="+2">Rates in this region</FONT>
-<% include('/edit/elements/rate_detail.html',
- 'regionnum' => $rate_region->regionnum,
-) %>
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ Rate plan
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Included<BR>minutes/calls</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Connection<BR>charge</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Connection<BR>charge for</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Charge per<BR>minute/call</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Granularity</FONT>
+ </TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">
+ <FONT SIZE=-1>Usage class</FONT>
+ </TH>
+ </TR>
+
+% foreach my $rate ( qsearch('rate', {}) ) {
+%
+% my $n = $rate->ratenum;
+% my $rate_detail = $rate->dest_detail($rate_region)
+% || new FS::rate_region { 'min_included' => 0,
+% 'min_charge' => 0,
+% 'sec_granularity' => '60'
+% };
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
% }
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/rate.cgi?<% $rate->ratenum %>"><% $rate->ratename %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <INPUT TYPE="text" SIZE=9 NAME="min_included<%$n%>" VALUE="<% $cgi->param("min_included$n") || $rate_detail->min_included |h %>">
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <%$money_char%><INPUT TYPE="text" SIZE=9 NAME="conn_charge<%$n%>" VALUE="<% $cgi->param("conn_charge$n") || $rate_detail->conn_charge |h %>">
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <SELECT NAME="conn_sec<%$n%>">
+% foreach my $conn_sec ( keys %conn_secs ) {
+% my $curr_value = $cgi->param("conn_sec$n") || $rate_detail->conn_sec;
+% my $selected = ($conn_sec==$curr_value) ? ' SELECTED' : '';
+ <OPTION VALUE="<% $conn_sec %>" <%$selected%>><% $conn_secs{$conn_sec} %></OPTION>
+% }
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <%$money_char%><INPUT TYPE="text" SIZE=6 NAME="min_charge<%$n%>" VALUE="<% $cgi->param("min_charge$n") || $rate_detail->min_charge |h %>">
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <SELECT NAME="sec_granularity<%$n%>">
+% foreach my $granularity ( keys %granularity ) {
+ <OPTION VALUE="<%$granularity%>"<% $granularity == ( $cgi->param("sec_granularity$n") || $rate_detail->sec_granularity ) ? ' SELECTED' : '' %>><%$granularity{$granularity}%>
+% }
+ </SELECT>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% include( '/elements/select-table.html',
+ 'element_name' => "classnum$n",
+ 'table' => 'usage_class',
+ 'name_col' => 'classname',
+ 'empty_label' => '(default)',
+ 'hashref' => { disabled => '' },
+ 'curr_value' => ( $cgi->param("classnum$n") ||
+ $rate_detail->classnum ),
+ )
+ %>
+ </TD>
+
+ </TR>
+
+% }
+
+</TABLE>
+
+
+<BR><BR>
+<INPUT TYPE="submit" VALUE="<% $rate_region->regionnum ? "Apply changes" : "Add region" %>">
+
+</FORM>
+
<% include('/elements/footer.html') %>
<%once>
+++ /dev/null
-<% include("/elements/header.html", { title => "$action Time Period" }) %>
-<% include("/elements/menubar.html",
- 'Rate plans' => "${p}browse/rate.cgi",
- ) %>
-<BR>
-<% include('/elements/error.html') %>
-<BR>
-
-<FORM METHOD="POST" ACTION="<% "${p}edit/process/rate_time.cgi" %>">
-<INPUT TYPE="hidden" NAME="ratetimenum" VALUE="<% $ratetimenum %>">
-<% ntable('#cccccc',2) %>
-<TABLE>
- <TR>
- <TH ALIGN="right">Period name</TH>
- <TD><INPUT TYPE="text" NAME="ratetimename" VALUE="<% $rate_time ? $rate_time->ratetimename : '' %>"></TD>
- </TR>
-</TABLE>
-<% include('/elements/auto-table.html',
- 'header' => [ '', 'Start','','', '','End','','' ],
- 'fields' => [ qw(sd sh sm sa ed eh em ea) ],
- 'select' => [ ($day, $hour, $min, $ampm) x 2 ],
- 'data' => \@data,
- ) %>
-<INPUT TYPE="submit" VALUE="<% $rate_time ? 'Apply changes' : 'Add period'%>">
-</FORM>
-<BR>
-<A HREF="<% "${p}edit/process/rate_time.cgi?ratetimenum=$ratetimenum;delete=1" %>">Delete this period</A>
-<% include('/elements/footer.html') %>
-
-<%init>
-my $ratetimenum = ($cgi->keywords)[0] || $cgi->param('ratetimenum') || '';
-my $action = 'Add';
-my $rate_time;
-my @data = ();
-my $day = [ 0 => 'Sun',
- 1 => 'Mon',
- 2 => 'Tue',
- 3 => 'Wed',
- 4 => 'Thu',
- 5 => 'Fri',
- 6 => 'Sat', ];
-my $hour = [ map( {$_, sprintf('%02d',$_) } 0..11 )];
-my $min = [ map( {$_, sprintf('%02d',$_) } 0,30 )];
-my $ampm = [ 0 => 'AM', 1 => 'PM' ];
-
-if($ratetimenum) {
- $action = 'Edit';
- $rate_time = qsearchs('rate_time', {ratetimenum => $ratetimenum})
- or die "ratetimenum $ratetimenum not found";
- if($cgi->param('error')) {
- my %vars = $cgi->Vars;
- foreach my $i (sort {$a <=> $b } map { /^sd(\d+)$/ } keys(%vars)) {
- push @data, [ @vars{"sd$i", "sh$i", "sm$i", "sa$i",
- "ed$i", "eh$i", "em$i", "ea$i"} ];
- }
- }
- else {
- foreach my $interval ($rate_time->intervals) {
- push @data, [ map { int($_/86400) % 7,
- int($_/3600) % 12,
- int($_/60) % 60,
- int($_/43200) % 2, }
- ( $interval->stime, $interval->etime )
- ];
- }
- }
-}
-
-</%init>
</TD>
</TR>
- <% include('/elements/tr-select.html',
- 'label' => 'On logout remove trash',
- 'field' => 'cgp_emptytrash',
- 'options' => $svc_acct->cgp_emptytrash_values,
- 'labels' => {
- '' => 'default (92 days)', #right?
- },
- 'curr_value' => $svc_acct->cgp_emptytrash,
- )
- %>
+ <TR>
+ <TD ALIGN="right">On logout remove trash</TD>
+ <TD><INPUT TYPE="text" NAME="cgp_emptytrash" VALUE="<% $svc_acct->cgp_emptytrash %>"></TD>
+ </TR>
<% include('/elements/tr-select.html',
'label' => 'Language',
<% include('/elements/tr-select.html',
'label' => 'Time zone',
'field' => 'cgp_timezone',
- 'options' => $svc_acct->cgp_timezone_values,
+ 'options' => [
+ 'HostOS',
+ '(+0100) Algeria/Congo',
+ '(+0200) Egypt/South Africa',
+ '(+0300) Saudi Arabia',
+ '(+0400) Oman',
+ '(+0500) Pakistan',
+ '(+0600) Bangladesh',
+ '(+0700) Thailand/Vietnam',
+ '(+0800) China/Malaysia',
+ '(+0900) Japan/Korea',
+ '(+1000) Queensland',
+ '(+1100) Micronesia',
+ '(+1200) Fiji',
+ '(+1300) Tonga/Kiribati',
+ '(+1400) Christmas Islands',
+ '(-0100) Azores/Cape Verde',
+ '(-0200) Fernando de Noronha',
+ '(-0300) Argentina/Uruguay',
+ '(-0400) Venezuela/Guyana',
+ '(-0500) Haiti/Peru',
+ '(-0600) Central America',
+ '(-0700) Arisona',
+ '(-0800) Adamstown',
+ '(-0900) Marquesas Islands',
+ '(-1000) Hawaii/Tahiti',
+ '(-1100) Samoa',
+ 'Asia/Afghanistan',
+ 'Asia/India',
+ 'Asia/Iran',
+ 'Asia/Iraq',
+ 'Asia/Israel',
+ 'Asia/Jordan',
+ 'Asia/Lebanon',
+ 'Asia/Syria',
+ 'Australia/Adelaide',
+ 'Australia/East',
+ 'Australia/NorthernTerritory',
+ 'Europe/Central',
+ 'Europe/Eastern',
+ 'Europe/Moscow',
+ 'Europe/Western',
+ 'GMT (+0000)',
+ 'Newfoundland',
+ 'NewZealand/Auckland',
+ 'NorthAmerica/Alaska',
+ 'NorthAmerica/Atlantic',
+ 'NorthAmerica/Central',
+ 'NorthAmerica/Eastern',
+ 'NorthAmerica/Mountain',
+ 'NorthAmerica/Pacific',
+ 'Russia/Ekaterinburg',
+ 'Russia/Irkutsk',
+ 'Russia/Kamchatka',
+ 'Russia/Krasnoyarsk',
+ 'Russia/Magadan',
+ 'Russia/Novosibirsk',
+ 'Russia/Vladivostok',
+ 'Russia/Yakutsk',
+ 'SouthAmerica/Brasil',
+ 'SouthAmerica/Chile',
+ 'SouthAmerica/Paraguay',
+ ],
'labels' => {
'' => 'default (HostOS)',
},
</TD>
</TR>
- <% include('/elements/tr-select.html',
+ <% include('/elements/tr-input-text.html',
'label' => 'On logout remove trash',
- 'field' => 'acct_def_cgp_emptytrash',
- 'options' => $svc_domain->cgp_emptytrash_values,
- 'labels' => {
- '' => 'default (92 days)', #right?
- },
'curr_value' => $svc_domain->acct_def_cgp_emptytrash,
)
%>
-
<% include('/elements/tr-select.html',
'label' => 'Language',
'field' => 'acct_def_cgp_language',
<% include('/elements/tr-select.html',
'label' => 'Time zone',
'field' => 'acct_def_cgp_timezone',
- 'options' => $svc_domain->cgp_timezone_values,
+ 'options' => [
+ 'HostOS',
+ '(+0100) Algeria/Congo',
+ '(+0200) Egypt/South Africa',
+ '(+0300) Saudi Arabia',
+ '(+0400) Oman',
+ '(+0500) Pakistan',
+ '(+0600) Bangladesh',
+ '(+0700) Thailand/Vietnam',
+ '(+0800) China/Malaysia',
+ '(+0900) Japan/Korea',
+ '(+1000) Queensland',
+ '(+1100) Micronesia',
+ '(+1200) Fiji',
+ '(+1300) Tonga/Kiribati',
+ '(+1400) Christmas Islands',
+ '(-0100) Azores/Cape Verde',
+ '(-0200) Fernando de Noronha',
+ '(-0300) Argentina/Uruguay',
+ '(-0400) Venezuela/Guyana',
+ '(-0500) Haiti/Peru',
+ '(-0600) Central America',
+ '(-0700) Arisona',
+ '(-0800) Adamstown',
+ '(-0900) Marquesas Islands',
+ '(-1000) Hawaii/Tahiti',
+ '(-1100) Samoa',
+ 'Asia/Afghanistan',
+ 'Asia/India',
+ 'Asia/Iran',
+ 'Asia/Iraq',
+ 'Asia/Israel',
+ 'Asia/Jordan',
+ 'Asia/Lebanon',
+ 'Asia/Syria',
+ 'Australia/Adelaide',
+ 'Australia/East',
+ 'Australia/NorthernTerritory',
+ 'Europe/Central',
+ 'Europe/Eastern',
+ 'Europe/Moscow',
+ 'Europe/Western',
+ 'GMT (+0000)',
+ 'Newfoundland',
+ 'NewZealand/Auckland',
+ 'NorthAmerica/Alaska',
+ 'NorthAmerica/Atlantic',
+ 'NorthAmerica/Central',
+ 'NorthAmerica/Eastern',
+ 'NorthAmerica/Mountain',
+ 'NorthAmerica/Pacific',
+ 'Russia/Ekaterinburg',
+ 'Russia/Irkutsk',
+ 'Russia/Kamchatka',
+ 'Russia/Krasnoyarsk',
+ 'Russia/Magadan',
+ 'Russia/Novosibirsk',
+ 'Russia/Vladivostok',
+ 'Russia/Yakutsk',
+ 'SouthAmerica/Brasil',
+ 'SouthAmerica/Chile',
+ 'SouthAmerica/Paraguay',
+ ],
'labels' => {
'' => 'default (HostOS)',
},
+++ /dev/null
-<%doc>
-
-Example:
-<% include('/elements/auto-table.html',
-
- ###
- # required
- ###
-
- 'header' => [ '#', 'Item', 'Amount' ],
- 'fields' => [ 'id', 'name', 'amount' ],
-
- ###
- # highly recommended
- ###
-
- 'size' => [ 4, 12, 8 ],
- 'maxl' => [ 4, 12, 8 ],
- 'align' => [ 'right', 'left', 'right' ],
-
- ###
- # optional
- ###
-
- 'data' => [ [ 1, 'Widget', 25 ],
- [ 12, 'Super Widget, 7 ] ],
- #or
- 'records' => [ qsearch('item', { } ) ],
- # or any other array of FS::Record objects
-
- 'select' => [ '',
- [ 1 => 'option 1',
- 2 => 'option 2', ...
- ], # options for second field
- '' ],
-
- 'prefix' => 'mytable_',
-) %>
-
-Values will be passed through as "mytable_id1", etc.
-</%doc>
-
-<TABLE ID="<% $prefix %>AutoTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
- <TR>
-% foreach (@header) {
- <TH><% $_ %></TH>
-% }
- </TR>
-% my $row = 0;
-% for ( $row = 0; $row < scalar @data; $row++ ) {
- <TR>
-% my $col = 0;
-% for ( $col = 0; $col < scalar @fields; $col++ ) {
-% my $id = $prefix . $fields[$col] . $row;
- <TD>
-% my @o = @{ $select[$col] };
-% if( @o ) {
- <SELECT NAME="<% $id %>" ID="<% $id %>">
-% while(@o) {
-% my $val = shift @o;
- <OPTION VALUE=<% $val %><%
-$val eq $data[$row][$col] ? ' SELECTED' : ''%>><% shift @o %></OPTION>
-% }
- </SELECT>
-% }
-% else {
- <INPUT TYPE = "text"
- NAME = "<% $id %>"
- ID = "<% $id %>"
- SIZE = <% $size[$col] %>
- MAXLENGTH = <% $maxl[$col] %>
- STYLE = "text-align:<% $align[$col] %>"
- VALUE = "<% $data[$row][$col] %>"
-% if( $opt{'autoadd'} ) {
- onchange = "possiblyAddRow(this);"
-% }
- >
- </TD>
-% }
-% }
- <TD>
- <IMG SRC = "<% "${p}images/cross.png" %>"
- ALT = "X"
- onclick = "deleteRow(this);"
- >
- </TD>
- </TR>
-% }
-</TABLE>
-% if( !$opt{'autoadd'} ) {
-<INPUT TYPE="button" VALUE="Add" onclick="<% $prefix %>addRow();"><BR>
-% }
-
-<SCRIPT TYPE="text/javascript">
- var <% $prefix %>rownum = <% $row %>;
- var <% $prefix %>table = document.getElementById('<% $prefix %>AutoTable');
- // last row is initially blank, clone it and remove it
- var <% $prefix %>_blank =
- <% $prefix %>table.rows[<% $prefix %>table.rows.length-1].cloneNode(true);
-% if( !$opt{'autoadd'} ) {
- <% $prefix %>table.deleteRow(<% $prefix %>table.rows.length-1);
-% }
-
-
-
- function rownum_of(obj) {
- return (obj.parentNode.parentNode.sectionRowIndex);
- }
-
- function <% $prefix %>possiblyAddRow(obj) {
- if ( <% $prefix %>rownum == rownum_of(obj) ) {
- <% $prefix %>addRow();
- }
- }
-
- function <% $prefix %>addRow() {
- var row = <% $prefix %>table.insertRow(-1);
- var cells = <% $prefix %>_blank.cells;
- for (i=0; i<cells.length; i++) {
- row.appendChild(cells[i].cloneNode(true));
- }
- <% $prefix %>rownum++;
- }
-
- function deleteRow(obj) {
- if(<% $prefix %>rownum == rownum_of(obj)) {
- <% $prefix %>addRow();
- }
- <% $prefix %>table.deleteRow(rownum_of(obj));
- <% $prefix %>rownum--;
- return(false);
- }
-
-</SCRIPT>
-
-<%init>
-my %opt = @_;
-
-my @header = @{ $opt{'header'} };
-my @fields = @{ $opt{'fields'} };
-my @data = ();
-if($opt{'data'}) {
- @data = @{ $opt{'data'} };
-}
-elsif($opt{'records'}) {
- foreach my $rec (@{ $opt{'records'} }) {
- push @data, [ map { $rec->getfield($_) } @fields ];
- }
-}
-# else @data = ();
-push @data, [ map {''} @fields ]; # make a blank row
-
-my $prefix = $opt{'prefix'};
-my @size = $opt{'size'} ? @{ $opt{'size'} } : (map {16} @fields);
-my @maxl = $opt{'maxl'} ? @{ $opt{'maxl'} } : @size;
-my @align = $opt{'align'} ? @{ $opt{'align'} } : (map {'right'} @fields);
-my @select = @{ $opt{'select'} || [] };
-foreach (0..scalar(@fields)-1) {
- $select[$_] ||= [];
-}
-</%init>
+++ /dev/null
-<%doc>
-
-Clickable link to bill a customer.
-
-Example:
-<% include( '/elements/bill.html',
- ###
- # required
- ###
- custnum => $custnum,
- label => 'Bill Now!',
-
- ###
- # recommended
- ###
- url => $p.'view/cust_main.cgi?'.$custnum,
-
- ###
- # optional, can contain any FS::cust_main::bill_and_collect options
- ###
- bill_opts => { 'batch_card' => 'yes' },
- formname => 'MyBillNowLink', # if for some reason you want this
-) %>
-
-</%doc>
-<FORM NAME="<%$formname%>" STYLE="display:inline">
-<% include('/elements/progress-init.html',
- $formname,
- [ 'custnum', @opt_keys ],
- $p.'misc/bill.cgi',
- $url ? { url => $url } : { message => $message },
- $formname, # use it as 'key'
-) %>
-<A HREF="javascript:void(0);" onclick="javascript:<%$formname%>process();"><%$label%></A>
-<INPUT TYPE="hidden" NAME="custnum" VALUE="<%$custnum%>">
-% foreach(@opt_keys) {
-<INPUT TYPE="hidden" NAME="<%$_%>" VALUE="<%$bill_opts->{$_}%>">
-% }
-</FORM>
-<%init>
-
-my %opt = @_;
-my $custnum = $opt{'custnum'};
-my $label = $opt{'label'};
-# formname no longer needs to be passed from outside, but we still
-# need one and it needs to be unique
-my $formname = $opt{'formname'} ||
- 'bill'.sprintf('%04d',int(rand(10000))).$custnum;
-my $url = $opt{'url'} || '';
-my $message = $opt{'message'} || 'Finished!';
-my $bill_opts = $opt{'bill_opts'} || {};
-my @opt_keys = keys(%$bill_opts);
-my @opt_vals = values(%$bill_opts);
-
-</%init>
Mail Relay Signal Mobile TLS POP IMAP MAPI
AirSync SIP XMPP WebMail XIMSS FTP ACAP PWD
LDAP RADIUS S/MIME WebCAL WebSite PBX HTTP
- MobilePBX YMedia
));
-#GIPS Media?
-
</%once>
<%init>
<INPUT TYPE="hidden" NAME="upload_fields" VALUE="<% join(',', @field) %>" />
% foreach (@field) {
-% if($param{'no_table'}) {
- <% shift @label %> <INPUT TYPE="file" NAME="<% $_ %>" />
-% }
-% else {
<TR>
<TH ALIGN="<% $param{'label_align'} || 'right' %>"><% shift @label %></TH>
<TD><INPUT TYPE="file" NAME="<% $_ %>" /></TD>
</TR>
-% }
% }
<DIV STYLE="display:<% $param{debug} ? 'visible' : 'none' %>">
font-weight:bold;
text-decoration:none;
overflow:visible;
- margin-left:6px;
- margin-right:6px;
}
a.fstab:hover {
text-decoration:none;
a:visited:hover.fsblackbutton
*/
a.fstabselected {
- background-color:#ffffff;
+ background-color:#f8f8f8;
color: #000000;
- border-top:1px solid #7e0079;
- border-left:1px solid #7e0079;
- border-right:1px solid #7e0079;
- border-bottom:1px solid #ffffff;
+ border:1px solid;
+ border-top-color:#7e0079;
+ border-left-color:#7e0079;
+ border-right-color:#7e0079;
+ border-bottom-color:#ffffff;
-moz-border-radius-topleft:8px;
-moz-border-radius-topright:8px;
-webkit-border-radius-topleft:8px;
font-weight:bold;
text-decoration:none;
overflow:visible;
- margin-left:6px;
- margin-right:6px;
}
a.fstabselected:hover {
text-decoration:none;
color: #000000;
}
-div.fstabs {
- padding-left:8px;
- border-bottom:1px solid #7e0079;
-}
-
-div.fstabcontainer {
- background-color:#ffffff;
- padding:8px;
- border-left:1px solid #7e0079;
- border-right:1px solid #7e0079;
- border-bottom:1px solid #7e0079;
- -moz-border-radius-bottomleft:8px;
- -moz-border-radius-bottomright:8px;
- -webkit-border-radius-bottomleft:8px;
- -webkit-border-radius-bottomright:8px;
- border-radius-bottomleft:8px;
- border-radius-bottomright:8px;
- -moz-box-shadow: #666666 1px 1px 2px;
- -webkit-box-shadow: #666666 1px 1px 2px;
- box-shadow: #666666 1px 1px 2px;
- filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', Direction=135, Strength=2);
-}
-
.background {
background-color:#f8f8f8;
}
-<%doc>
-
-Example:
-
- include( '/elements/header-popup.html',
- {
- 'title' => 'Title',
- 'menubar' => \@menubar,
- 'etc' => '', #included in <BODY> tag, for things like onLoad=
- 'head' => '', #included before closing </HEAD> tag
- 'nobr' => 0, #1 for no <BR><BR> after the title
- }
- );
-
- #old-style
- include( '/elements/header.html', 'Title', $menubar, $etc, $head);
-
-</%doc>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
oFCKeditor.BasePath = '<% $p %>elements/fckeditor/';
oFCKeditor.Config['SkinPath'] = '<% $p %>elements/fckeditor/editor/skins/silver/';
-% if ( $opt{'width'} ) {
- oFCKeditor.Width = '<% $opt{'width'} %>';
-% }
oFCKeditor.Height = '<% $opt{'height'} || 420 %>';
oFCKeditor.Config['StartupFocus'] = true;
oFCKeditor.Config['EnterMode'] = 'br';
tie my %report_inventory, 'Tie::IxHash',
'Inventory by agent' => [ $fsurl.'search/report_agent_inventory.html', '' ],
- 'Inventory activity' => [ $fsurl.'search/report_h_inventory_item.html', '' ],
+ #'Inventory activity' => [ $fsurl.'search/report_h_inventory_item.html', '' ],
;
tie my %report_rating, 'Tie::IxHash',
$tools_menu{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_date;open=1;intransit=1', 'Process credit card and electronic check batches' ]
if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
&& $curuser->access_right('Process batches');
-$tools_menu{'Process invoice batches'} = [ $fsurl.'search/bill_batch.cgi' ]
- if ( $conf->exists('invoice_print_pdf') );
$tools_menu{'Job Queue'} = [ $fsurl.'search/queue.html', 'View pending job queue' ]
if $curuser->access_right('Job queue');
$tools_menu{'Ticketing'} = [ \%tools_ticketing, 'Ticketing tools' ]
'Rate plans' => [ $fsurl.'browse/rate.cgi', 'Manage rate plans' ],
'Regions and prefixes' => [ $fsurl.'browse/rate_region.html', 'Manage regions and prefixes' ],
'Usage classes' => [ $fsurl.'browse/usage_class.html', 'Usage classes define groups of usage for taxation.' ],
- 'Time periods' => [ $fsurl.'browse/rate_time.html', 'Time periods define days and hours for rate plans' ],
'Edit rates with Excel' => [ $fsurl.'misc/rate_edit_excel.html', 'Download and edit rates with Excel, then upload changes.' ], #"Edit with Excel" ?
;
;
tie my %config_misc, 'Tie::IxHash';
-$config_misc{'Message templates'} = [ $fsurl.'browse/msg_template.html', 'Templates for customer notices' ]
- if $curuser->access_right('Edit templates')
- || $curuser->access_right('Edit global templates')
- || $curuser->access_right('Configuration');
-$config_misc{'Tags'} = [ $fsurl.'browse/part_tag.html', '' ]
- if $curuser->access_right('Configuration');
$config_misc{'Advertising sources'} = [ $fsurl.'browse/part_referral.html', 'Where a customer heard about your service.' ]
if $curuser->access_right('Edit advertising sources')
|| $curuser->access_right('Edit global advertising sources');
if ( $curuser->access_right('Configuration') ) {
$config_misc{'Virtual fields'} = [ $fsurl.'browse/part_virtual_field.cgi', 'Locally defined fields', ];
- $config_misc{'Error catalog'} = [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels' ];
+ $config_misc{'Message catalog'} = [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels' ];
}
$config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_class.html', 'Setup inventory classes and stock inventory' ]
if $curuser->access_right('Edit inventory')
$config_menu{'Phone'} = [ \%config_phone, '' ]
if ( $curuser->access_right('Configuration') );
$config_menu{'Miscellaneous'} = [ \%config_misc, '' ]
- if $curuser->access_right('Configuration' )
- || $curuser->access_right('Edit advertising sources')
+ if $curuser->access_right('Edit advertising sources')
|| $curuser->access_right('Edit global advertising sources');
</%doc>
%if ( $opt->{'newstyle'} ) {
+% my $s = '<FONT STYLE="border-bottom:1px solid #7e0079">';
- <DIV CLASS="fstabs">
- <% join('', @html ) %>
- </DIV>
+ <% join("$s </FONT>", ( '', @html, '' ) ) %>
+ <BR>
%} else {
+++ /dev/null
-<INPUT TYPE="hidden" NAME="<% $opt{'field'} %>" ID="<%$id%>" VALUE="<%$value%>">
-<TABLE BGCOLOR="#FFFFFF" ID="showcolor<%$unum%>">
-<TR>
- <TD STYLE="border:1px solid blue;background-color:#<%$value%>" WIDTH=16 HEIGHT=16 ID="currcolor<%$unum%>"></TD>
- <TD> <A HREF="javascript:void(0);" onClick="change_clicked<%$unum%>()">change</A></TD>
-</TR>
-</TABLE>
-<TABLE BGCOLOR="#FFFFFF" ID="pickcolor<%$unum%>" STYLE="display:none">
-% for (1..$rows) {
- <TR>
-% for (1..$cols) {
-% last unless @colors;
-% my $color = shift(@colors);
- <TD STYLE="border:1px solid blue;cursor:pointer;cursor:hand" BGCOLOR="#<% $color %>" WIDTH=16 HEIGHT=16 onClick="color_clicked<%$unum%>('<%$color%>')"></TD>
-% }
- </TR>
-% }
-</TABLE>
-<SCRIPT TYPE="text/javascript">
-
- function change_clicked<%$unum%>() {
- document.getElementById('showcolor<%$unum%>').style.display = 'none';
- document.getElementById('pickcolor<%$unum%>').style.display = '';
- }
-
- function color_clicked<%$unum%>(color) {
- document.getElementById('<%$id%>').value = color; //update hidden
- if ( color == '' ) { color = 'ffffff'; }
- document.getElementById('currcolor<%$unum%>').style.backgroundColor = '#' + color;
- document.getElementById('showcolor<%$unum%>').style.display = '';
- document.getElementById('pickcolor<%$unum%>').style.display = 'none';
- }
-
-</SCRIPT>
-<%init>
-
-my %opt = @_;
-
-my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value};
-
-my $unum = int(rand(100000));
-
-my $id = $opt{'id'} || $opt{'field'}.$unum;
-
-my @colors = (
- '', #none/white
- 'FF6666', #red
- 'FF9966', #orange
- 'FFFF66', #yellow
- '66FF66', #green
- '66FFFF', #cyan?
- '6666FF', #blue
- 'CC66FF', #purple? FF66FF looks more like pink.
-);
-
-my $rows = 2;
-
-my $cols = int(.5+scalar(@colors)/$rows);
-
-</%init>
-<%doc>
-Example:
-In misc/something.html:
-
- <FORM NAME="MyForm">
- <INPUT TYPE="hidden" NAME="recordnum" VALUE="42">
- <INPUT TYPE="hidden" NAME="what_to_do" VALUE="delete">
- <% include( '/elements/progress-init.html',
- 'MyForm',
- [ 'recordnum', 'what_to_do' ],
- $p.'misc/process_something.html',
- { url => $p.'where_to_go_next.html' },
- #or { message => 'Finished!' },
- ) %>
- </FORM>
- <SCRIPT TYPE="text/javascript>process();</SCRIPT>
-
-In misc/process_something.html:
-
-<%init>
-my $server = FS::UI::Web::JSRPC->new('FS::something::process_whatever', $cgi);
-</%init>
-<% $server->process %>
-
-In FS/something.pm:
-
-sub process_whatever { #class method
- my $job = shift;
- my $param = thaw(base64_decode(shift));
- # param = { 'recordnum' => 42, 'what_to_do' => delete }
- # make use of this as you like
- do_phase1;
- $job->update_statustext(20);
- do_phase2;
- $job->update_statustext(40);
- do_phase3;
- $job->update_statustext(60);
- # etc.
- return 'this value will be ignored';
-}
-
-</%doc>
<% include('/elements/xmlhttp.html',
'method' => 'POST',
'url' => $action,
alert('job done but no url or message specified');
% }
- } else if ( status.indexOf('done') > -1 ) {
-
- document.getElementById("progress_message").innerHTML = "Loading report";
- document.getElementById("progress_bar").innerHTML = '';
- document.getElementById("progress_percent").innerHTML = '';
- document.getElementById("progress_jobnum").innerHTML = '';
- window.top.location.href = statustext.substr(8, statustext.length-18);
-
} else if ( status.indexOf('error') > -1 ) {
document.getElementById("progress_message").innerHTML = '<FONT SIZE="+1" COLOR="#FF0000">Error: ' + statustext + '</FONT>';
document.getElementById("progress_bar").innerHTML = '';
<INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
<% include( 'select.html',
- 'field' => $name.'_conditionname',
- 'id' => $id.'_conditionname',
+ 'field' => $name.'_condition',
+ 'id' => $id.'_condition',
'options' => \@conditions,
- 'curr_value' => $conditionname,
+ 'curr_value' => $condition,
'labels' => { '' => 'Select Condition' },
'onchange' => $name.'_changed',
)
$cgp_rule_condition = new FS::cgp_rule_condition {};
}
-my $conditionname = scalar($cgi->param($name.'_conditionname'))
- || $cgp_rule_condition->conditionname;
+my $condition = scalar($cgi->param($name.'_condition'))
+ || $cgp_rule_condition->condition;
-my @op = &$cond2op($conditionname);
+my @op = &$cond2op($condition);
my $disabled = scalar(@op) ? '' : 1;
my $style = $disabled ? 'visibility:hidden' : '';
+++ /dev/null
-<% include( '/elements/select-table.html',
- 'table' => 'part_tag',
- 'name_col' => 'tagname', #tagname - tagdesc??
- 'multiple' => 1,
- #'value' => $agentnum || '',
- #'agent_virt' => 1,
- 'hashref' => { 'disabled' => '' },
- 'order_by' => ' ORDER BY tagname',
- %opt,
- )
-%>
-<%init>
-
-my %opt = @_;
-#my $agentnum = $opt{'curr_value'} || $opt{'value'};
-
-$opt{'records'} = delete $opt{'part_tag'}
- if $opt{'part_tag'};
-
-</%init>
+++ /dev/null
-<% include('tr-td-label.html', @_ ) %>
-
- <TD <% $cell_style %>>
-
- <% include('htmlarea.html', @_ ) %>
-
- </TD>
-
-</TR>
-
-<%init>
-
-my %opt = @_;
-
-my $onchange = $opt{'onchange'}
- ? 'onChange="'. $opt{'onchange'}. '(this)"'
- : '';
-
-#my $rows = $opt{'rows'} ? 'ROWS="'.$opt{'rows'}.'"' : '';
-#my $cols = $opt{'cols'} ? 'COLS="'.$opt{'cols'}.'"' : '';
-
-my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
-#my $curr_value = $opt{'curr_value'};
-
-</%init>
+++ /dev/null
-<% include('tr-td-label.html', @_ ) %>
- <TD <% $colspan %> <% $cell_style %> ID="<% $opt{input_id} || $opt{id}.'_input0' %>"><% include('pickcolor.html', @_ ) %></TD>
-<%init>
-
-my %opt = @_;
-
-my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
-
-my $colspan = $opt{'colspan'} ? 'COLSPAN="'.$opt{'colspan'}.'"' : '';
-
-</%init>
-<%doc>
-
-Example:
-
- include( '/elements/tr-select-agent.html',
-
- #recommended to keep things "sticky" on errors
- 'curr_value' => $curr_value,
-
- ##
- # optional
- ##
-
- 'label' => 'Agent for this thing',
- 'empty_label' => 'Select agent', #override default
- 'disable_empty' => 1,
-
- #set to 'None' or something to override default of showing all agents
- #for employees w/ 'View customers of all agents' right
- viewall_right => 'None',
-
- );
-
-</%doc>
% if ( scalar(@agents) == 1 ) {
<INPUT TYPE="hidden" NAME="<% $opt{'field'} || 'agentnum' %>" VALUE="<% $agents[0]->agentnum %>">
my %opt = @_;
my $agentnum = $opt{'curr_value'} || $opt{'value'};
-my @agents =
- $opt{'agents'}
- ? @{ $opt{'agents'} }
- : $FS::CurrentUser::CurrentUser->agents(
- 'viewall_right' => $opt{'viewall_right'},
- );
+my @agents = $opt{'agents'}
+ ? @{ $opt{'agents'} }
+ : $FS::CurrentUser::CurrentUser->agents;
</%init>
+++ /dev/null
-% if ( $curuser->access_right('Edit customer tags') && @part_tag ) {
-
- <TR>
- <TD ALIGN="right"><% $opt{'label'} || 'Tags' %></TD>
- <TD>
- <% include( '/elements/select-cust_tag.html',
- 'curr_value' => \@curr_tagnum,
- 'part_tag' => \@part_tag,
- %opt,
- )
- %>
- </TD>
- </TR>
-
-% } else {
-
-% foreach my $tagnum (@curr_tagnum) {
- <INPUT TYPE="hidden" NAME="tagnum" VALUE="<% $tagnum %>">
-% }
-
-% }
-<%init>
-
-my $curuser = $FS::CurrentUser::CurrentUser;
-
-my %opt = @_;
-my $cgi = $opt{'cgi'};
-
-my @curr_tagnum = ();
-if ( $cgi->param('error') ) {
- @curr_tagnum = $cgi->param('tagnum');
-} elsif ( $opt{'custnum'} ) {
- @curr_tagnum = map $_->tagnum,
- qsearch('cust_tag', { 'custnum' => $opt{'custnum'} } );
-}
-
-my $extra_sql = "WHERE disabled IS NULL OR disabled = '' ";
-$extra_sql .= ' OR tagnum IN ('. join(',', @curr_tagnum). ')' if @curr_tagnum;
-
-my @part_tag = qsearch({
- 'table' => 'part_tag',
- 'hashref' => {},
- 'extra_sql' => $extra_sql,
-});
-
-</%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $link = "${p}search/cust_bill_pkg.cgi?nottax=1";
+my $link = "${p}search/cust_bill_pkg.cgi?nottax=1;include_comp_cust=1";
my $bottom_link = "$link;";
my $use_override = $cgi->param('use_override') ? 1 : 0;
<% include('/elements/header.html', 'Billing Main' ) %>
-<% include('/elements/dashboard-install_welcome.html') %>
-
<% include('/elements/dashboard-toplist.html') %>
% my $sth = dbh->prepare(
-<% $server->process %>
+%if ( $error ) {
+% errorpage($error);
+%} else {
+<% $cgi->redirect(popurl(2). "view/cust_main.cgi?$custnum") %>
+%}
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Bill customer now');
-my $server = FS::UI::Web::JSRPC->new('FS::cust_main::process_bill_and_collect', $cgi);
-</%init>
+#untaint custnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d*)$/;
+my $custnum = $1;
+my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum});
+die "Can't find customer!\n" unless $cust_main;
+
+my $conf = new FS::Conf;
+
+my $error = $cust_main->bill_and_collect( 'fatal' => 'return',
+ 'retry' => 'yes',
+ );
+
+ #'invoice-time'=>$time,
+ #'batch_card'=> 'yes',
+ #'batch_card'=> 'no',
+ #'report_badcard'=> 'yes',
+ #'retry_card' => 'yes',
+
+ #this is used only by cust_main::batch_card
+ #need to pick & create an actual config
+ #value if we're going to turn this on
+ #("realtime-backend" doesn't exist,
+ # "backend-realtime" is for something
+ # entirely different)
+ #'realtime' => $conf->exists('realtime-backend'),
+
+</%init>
+++ /dev/null
-% if ( $error ) {
-0,"<% $error %>",,
-% } else {
-1,"CDR import successful",<% $cdr_batch->cdrbatchnum %>,"<% $cdrbatch %>"
-% }
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Import');
-
-my $error = '';
-my $cdr_batch;
-my $cdrbatch = '';
-
-{
-
- my $filename = $cgi->param('cdr_file');
- unless ( $filename ) {
- $error = "No cdr_file filename";
- last;
- }
-
- my $fh = $cgi->upload('cdr_file');
- unless ( defined($fh) ) {
- $error = 'No cdr_file file';
- last;
- }
-
- #i should probably be transactionalized.
-
- my $csv = new Text::CSV_XS or die Text::CSV->error_diag;
-
- $cdrbatch = time2str('post-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
- $cdr_batch = new FS::cdr_batch { 'cdrbatch' => $cdrbatch };
- $error = $cdr_batch->insert and last;
-
- chomp(my $hline = scalar(<$fh>));
- $csv->parse($hline);
- my @header = $csv->fields;
-
- #while ( my $row = $csv->getline($fh) ) {
- while (<$fh>) {
-
- $csv->parse($_);
- my @row = $csv->fields;
-
- my $cdr = new FS::cdr { 'cdrbatchnum' => $cdr_batch->cdrbatchnum };
- $cdr->set( lc($_) => shift(@row) ) foreach @header;
-
- $error = $cdr->insert and last;
-
- }
-
-}
-
-$error =~ s/"/""/g; #CSV
-
-</%init>
+++ /dev/null
-<% include("/elements/header.html",'Call Detail Record - POST Import') %>
-
-<FORM METHOD="POST" ACTION="cdr-post.cgi" enctype="multipart/form-data">
-
- cdr_file: <INPUT TYPE="file" NAME="cdr_file"><BR><BR>
-
- <INPUT TYPE="submit" VALUE="upload">
-
-</FORM>
-
-<% include("/elements/footer.html") %>
+++ /dev/null
-% if ( $error ) {
-% errorpage($error);
-% } else {
-<% $cgi->redirect($p. "browse/cgp_rule.html?svcnum=". $svcnum) %>
-% }
-<%init>
-
-# :/ needs agent-virt so you can't futz with arbitrary rules
-
-#die "access denied"
-# unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service');
-
-#untaint svcnum and clone
-$cgi->param('svcnum') =~ /^(\d+)$/ || die "Illegal svcnum";
-my $svcnum = $1;
-$cgi->param('clone') =~ /^(\d+)$/ || die "Illegal clone";
-my $clone = $1;
-
-my @cgp_rule = qsearch('cgp_rule', { 'svcnum' => $clone } );
-
-my $error = '';
-foreach my $cgp_rule ( @cgp_rule ) {
- $error = $cgp_rule->clone( $svcnum );
- last if $error;
-}
-
-</%init>
#die "access denied"
# unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service');
-#untaint rulenum
+#untaint devicenum
my($query) = $cgi->keywords;
$query =~ /^(\d+)$/ || die "Illegal rulenum";
my $rulenum = $1;
+++ /dev/null
-% if ( $error ) {
-% errorpage($error);
-% } else {
-<% header('Rate deleted') %>
- <SCRIPT TYPE="text/javascript">
- window.top.location.reload();
- </SCRIPT>
- </BODY></HTML>
-% }
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
-
-my ($query) = $cgi->keywords;
-$query =~ /^(\d+)$/ or die "Illegal ratedetailnum";
-my $rate_detail = FS::rate_detail->by_key($1);
-my $error = $rate_detail->delete;
-
-</%init>
%# <INPUT TYPE="hidden" NAME="itembatch" VALUE="<% $itembatch %>">
- <% include('/elements/tr-select-agent.html',
- 'viewall_right' => 'None',
- )
- %>
+ <% include('/elements/tr-select-agent.html') %>
<% include( '/elements/file-upload.html',
'field' => 'file',
+++ /dev/null
-<% include('/elements/header.html', {
- 'title' => "Customer $custnum status",
- }) %>
-
-<% include('/elements/small_custview.html', $custnum, '', 1) %>
-<BR>
-
-<table style="border:1px solid #000000">
-% foreach my $key (keys %$return) {
-% my $value = $return->{$key};
-% $value = join(', ', @$value) if ref($value) eq 'ARRAY';
- <TR>
- <TD ALIGN="right"><% $key %>:</TD>
- <TD><B><% $value %></B></TD>
- </TR>
-% }
-</table>
-
-<% include('/elements/footer.html') %>
-<%init>
-
-my $return;
-
-my($custnum, $svcnum) = $cgi->keywords;
-if ( $custnum =~ /^(\d+)$/ ) {
-
- use FS::Maestro;
- $return = FS::Maestro::customer_status($1, $svcnum);
-
-} else {
- $return = { 'error' => 'No custnum' };
-}
-
-</%init>
+++ /dev/null
-<% $uri->query %>
-<%init>
-
-my $uri = new URI;
-
-my($custnum, $svcnum) = $cgi->keywords;
-if ( $custnum =~ /^(\d+)$/ ) {
-
- use FS::Maestro;
- $uri->query_form( FS::Maestro::customer_status($1) );
-
-} else {
- $uri->query_form( { 'error' => 'No custnum' } );
-}
-
-</%init>
+++ /dev/null
-<% objToJson( $return ) %>
-<%init>
-
-my $return;
-
-my($custnum, $svcnum) = $cgi->keywords;
-if ( $custnum =~ /^(\d+)$/ ) {
-
- use FS::Maestro;
- $return = FS::Maestro::customer_status($1, $svcnum);
-
-} else {
- $return = { 'error' => 'No custnum' };
-}
-
-</%init>
+++ /dev/null
-% die "access denied"
-% unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
-% my $server = FS::UI::Web::JSRPC->new('FS::bill_batch::process_print_pdf', $cgi);
-<% $server->process %>
-<%init></%init>
$payinfo = $1;
validate($payinfo)
or errorpage(gettext('invalid_card')); # . ": ". $self->payinfo;
-
errorpage(gettext('unknown_card_type'))
- if $payinfo !~ /^99\d{14}$/ #token
- && cardtype($payinfo) eq "Unknown";
+ if $payinfo !~ /^99\d{14}$/ && cardtype($payinfo) eq "Unknown";
if ( defined $cust_main->dbdef_table->column('paycvv') ) {
if ( length($cgi->param('paycvv') ) ) {
$error = $cust_main->charge($amount, "Recharge " . $svc_acct->label,
$description, $part_pkg->taxclass);
- $error ||= "invalid $_" foreach grep { $rhash{$_} !~ /^\d*$/ } keys %rhash;
if ($part_pkg->option('recharge_reset', 1)) {
$error ||= $svc_acct->set_usage(\%rhash, 'null' => 1);
}else{
-<% $server->process %>
+% if ( $error ) {
+% errorpage($error);
+% } else {
+ <% include('/elements/header.html','Batch results upload successful') %>
+ <% include('/elements/footer.html') %>
+% }
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Process batches');
-my $server =
- new FS::UI::Web::JSRPC 'FS::pay_batch::process_import_results', $cgi;
+my $error;
+
+my $fh = $cgi->upload('batch_results');
+$error = 'No file uploaded' unless defined($fh);
+
+unless ( $error ) {
+
+ $cgi->param('batchnum') =~ /^(\d+)$/;
+ my $batchnum = $1;
+
+ my $pay_batch = qsearchs( 'pay_batch', { 'batchnum' => $batchnum } );
+ if ( ! $pay_batch ) {
+ $error = "batchnum $batchnum not found";
+ } elsif ( $pay_batch->status ne 'I' ) {
+ $error = "batch $batchnum is not in transit";
+ } else {
+ $error = $pay_batch->import_results(
+ 'filehandle' => $fh,
+ 'format' => $cgi->param('format'),
+ );
+ }
+
+}
</%init>
-<% $response_xml %>\
-<%init>
+%
+%
+% my $request_xml = $cgi->param('POSTDATA');
+%
+% #$r->log_error($request_xml);
+%
+% my $fsxmlrpc = new FS::XMLRPC;
+% my ($error, $response_xml) = $fsxmlrpc->serve($request_xml);
+%
+% #$r->log_error($error) if $error;
+%
+% http_header('Content-Type' => 'text/xml',
+% 'Content-Length' => length($response_xml));
+%
+% print $response_xml;
+%
+%
-my $request_xml = $cgi->param('POSTDATA');
-
-#warn $request_xml;
-
-my $fsxmlrpc = new FS::XMLRPC;
-my ($error, $response_xml) = $fsxmlrpc->serve($request_xml);
-
-#warn $error;
-
-http_header('Content-Type' => 'text/xml',
- 'Content-Length' => length($response_xml));
-
-</%init>
% }
<%init>
-if ( FS::Conf->new->exists('disable_acl_changes') ) {
- errorpage("Preference changes disabled in public demo");
- die "shouldn't be reached";
-}
-
my $error = '';
my $access_user = '';
% if ( $part eq 'IA' ) {
% for ( my $tech = 0; $tech < scalar(@technology_option); $tech++ ) {
% next unless $technology_option[$tech];
-% my $url = &{$url_mangler}($part);
+% my $url = &{$url_mangler}($cgi->self_url, $part);
% if ( $type eq 'xml' ) {
<<% 'Part_IA_'. chr(65 + $tech) %>>
% }
% if ( $type eq 'xml' ) {
<<% 'Part_'. uc($part) %>>
% }
-% my $url = &{$url_mangler}($part);
+% my $url = &{$url_mangler}($cgi->self_url, $part);
<% include( "477part${part}.html", 'url' => $url ) %>
% if ( $type eq 'xml' ) {
</<% 'Part_'. uc($part) %>>
my $xlsname = '477report';
my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
my $url_mangler = sub {
- my $part = shift;
- my $url = $cgi->url('-path_info' => 1, '-full' => 1);
+ my ($url, $part) = (shift, shift);
$url =~ s/477\./477part$part./;
$url;
};
'xml_elements' => [ 'zip codes' ],
'no_field_elements' => 1,
'fields' => [ 'zip' ],
- 'url' => $opt{url} || '',
+ 'url' => $opt{url} || $cgi->self_url,
)
%>
[ $link, $link_suffix ],
[ $link, $link_suffix ],
],
- 'url' => $opt{url} || '',
+ 'url' => $opt{url} || $cgi->self_url,
'xml_row_element' => 'Datarow',
)
%>
+++ /dev/null
-<% include( 'elements/search.html',
- 'title' => 'Invoice Batches',
- 'name_singular' => 'batch',
- 'query' => { 'table' => 'bill_batch',
- 'hashref' => $hashref,
- 'extra_sql' => $extra_sql.
- 'ORDER BY batchnum DESC',
- },
- 'count_query' => "$count_query $extra_sql",
- 'header' => [ 'Batch',
- 'Item Count',
- 'Status',
- '',
- ],
- 'align' => 'rrcc',
- 'fields' => [ 'batchnum',
- sub {
- my $st = "SELECT COUNT(*) from cust_bill_batch WHERE batchnum=" . shift->batchnum;
- my $sth = dbh->prepare($st)
- or die dbh->errstr. "doing $st";
- $sth->execute
- or die "Error executing \"$st\": ". $sth->errstr;
- $sth->fetchrow_arrayref->[0];
- },
- sub {
- $statusmap{shift->status};
- },
- sub { shift->status eq 'O' ?
- 'Download and close' : 'Download'
- },
- ],
- 'links' => [
- $link,
- $link,
- $link,
- $dlink,
- ],
- 'style' => [
- '',
- '',
- '',
- sub { shift->status eq 'O' ? "b" : '' },
- ],
- 'really_disable_download' => 1,
- )
-
-%>
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
-
-my %statusmap = ('O'=>'Open', 'R'=>'Closed');
-my $hashref = {};
-my $count_query = 'SELECT COUNT(*) FROM bill_batch';
-
-my $extra_sql = ''; # may add something here later
-my $link = [ "${p}view/bill_batch.cgi?batchnum=", 'batchnum' ];
-my $dlink = sub {
- [ "${p}view/bill_batch.cgi?magic=print;".
- (shift->status eq 'O' ? 'close=1;' : '').
- 'batchnum=',
- 'batchnum']
-};
-</%init>
$search =
"NOT EXISTS ( SELECT 1 FROM cdr_termination WHERE $where_term )";
- } elsif ( $status =~ /^([\w ]+)$/ ) {
+ } elsif ( $cgi->param('freesidestatus') =~ /^([\w ]+)$/ ) {
#false lazienss w/cdr_termination.pm (i should be a part_termination method)
my $where_term =
push @where, "_date >= $beginning",
"_date <= $ending";
+push @where , " payby != 'COMP' "
+ unless $cgi->param('include_comp_cust');
+
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
push @where, "cust_main.agentnum = $1";
}
push @where, "cust_bill._date >= $beginning",
"cust_bill._date <= $ending";
+push @where , " payby != 'COMP' "
+ unless $cgi->param('include_comp_cust');
+
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
push @where, "cust_main.agentnum = $1";
}
'LEFT JOIN pay_batch USING ( batchnum ) ' .
"$search ORDER BY $orderby";
-my $html_init = '<TABLE>';
+my $html_init = '';
if ( $pay_batch ) {
my $fixed = $conf->config('batch-fixed_format-'. $pay_batch->payby);
if (
&& $FS::CurrentUser::CurrentUser->access_right('Redownload resolved batches')
)
) {
- $html_init .= qq!<TR><FORM ACTION="$p/misc/download-batch.cgi" METHOD="POST">!;
+ $html_init .= qq!<FORM ACTION="$p/misc/download-batch.cgi" METHOD="POST">!;
if ( $fixed ) {
$html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
} else {
- $html_init .= qq!Download batch in format !.
- qq!<SELECT NAME="format">!.
+ $html_init .= qq!Download batch in format <SELECT NAME="format">!.
qq!<OPTION VALUE="">Default batch mode</OPTION>!.
qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV file for TD Canada Trust Merchant PC Batch</OPTION>!.
qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
qq!<OPTION VALUE="RBC">Royal Bank of Canada PDS</OPTION>!.
qq!</SELECT>!;
}
- $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum"><INPUT TYPE="submit" VALUE="Download"></FORM><BR><BR></TR>!;
+ $html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum"><INPUT TYPE="submit" VALUE="Download"></FORM><BR>!;
}
if (
&& $FS::CurrentUser::CurrentUser->access_right('Reprocess batches')
)
) {
- $html_init .= '<TR>'.
- include('/elements/form-file_upload.html',
- 'name' => 'FileUpload',
- 'action' => "$p/misc/upload-batch.cgi",
- 'num_files' => 1,
- 'fields' => [ 'batchnum', 'format' ],
- 'message' => 'Batch results uploaded.',
- ) .
- 'Upload results<BR></TR><TR>'.
- include('/elements/file-upload.html',
- 'field' => 'file',
- 'label' => 'Filename',
- 'no_table'=> 1
- ).
- '<BR></TR>'
- ;
+ $html_init .= qq!<FORM ACTION="$p/misc/upload-batch.cgi" METHOD="POST" ENCTYPE="multipart/form-data">!.
+ qq!Upload results<BR>!.
+ qq!Filename <INPUT TYPE="file" NAME="batch_results"><BR>!;
if ( $fixed ) {
$html_init .= qq!<INPUT TYPE="hidden" NAME="format" VALUE="$fixed">!;
} else {
- # should pull this from %import_info
- $html_init .= qq!<TR>Format !.
- qq!<SELECT NAME="format">!.
+ $html_init .= qq!Format <SELECT NAME="format">!.
qq!<OPTION VALUE="">Default batch mode</OPTION>!.
qq!<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV results from TD Canada Trust Merchant PC Batch</OPTION>!.
qq!<OPTION VALUE="csv-chase_canada-E-xactBatch">CSV file for Chase Canada E-xactBatch</OPTION>!.
qq!<OPTION VALUE="ach-spiritone">Spiritone ACH batch</OPTION>!.
qq!<OPTION VALUE="paymentech">Chase Paymentech XML</OPTION>!.
qq!<OPTION VALUE="RBC">Royal Bank of Canada PDS</OPTION>!.
- qq!</SELECT><BR></TR>!;
+ qq!</SELECT><BR>!;
}
$html_init .= qq!<INPUT TYPE="hidden" NAME="batchnum" VALUE="$batchnum">!;
- $html_init .= '<TR> <INPUT TYPE="submit" VALUE="Upload"></FORM><BR> </TR>';
+ $html_init .= '<INPUT TYPE="submit" VALUE="Upload"></FORM><BR>';
}
- $html_init .= '</TABLE>'
+
}
if ($pay_batch) {
# '</table>';
# },
sub {
- my $cust_pkg = shift;
- my $type = $cgi->param('_type') || '';
- if ($type =~ /xls|csv/) {
- my $cust_svc = $cust_pkg->primary_cust_svc;
- if($cust_svc) {
- return join ": ",($cust_svc->label)[0,1];
- }
- else {
- return '';
- }
- }
- else {
[ map {
[
{ 'data' => $_->[0]. ':',
$_->[2]. '.cgi?'. $_->[3],
},
];
- } $cust_pkg->labels
+ } shift->labels
];
- }
- }
+ },
],
'color' => [
'',
my( $start, $end ) = @_;
"SQL EXPRESSION BASED ON $start AND $end";
- # where $start and $end are unix timestamps
};
</%doc>
}
push @where,
- call_range_sub($range_sub, $days, 0, 'offset' => $offset, 'no_as'=>1). ' > 0'; # != 0';
+ call_range_sub($range_sub, $days + $offset, 0, 'no_as'=>1). ' > 0'; # != 0';
}
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
# )
sub call_range_sub {
- my($range_sub, $startdays, $enddays, %opt) = @_;
+ my($range_sub, $start, $end, %opt) = @_;
- my $as = $opt{'no_as'} ? '' : " AS rangecol_${startdays}_$enddays";
+ my $as = $opt{'no_as'} ? '' : " AS rangecol_${start}_$end";
- my $offset = $opt{'offset'} || 0;
- # Always use $offset - 1day + 1sec = the last second of that day
- my $cutoff = DateTime->now->set(hour => 23, minute => 59, second => 59);
- $cutoff->subtract(days => $offset);
-
- my $start = $cutoff->clone;
- $start->subtract(days => $startdays);
-
- my $end = $cutoff->clone;
- $end->subtract(days => $enddays);
-
- #warn "offset $offset (".$cutoff->epoch."), range $startdays-$enddays (".$start->epoch . '-' . ($enddays ? $end->epoch : '').")\n";
- my $sql = &{$range_sub}( $start->epoch,
- $enddays ? $end->epoch : '',
- $cutoff->epoch ); #%opt?
+ my $sql = &{$range_sub}( $start, $end, $opt{'offset'} ); #%opt?
$sql = "SUM($sql)" if $opt{'sum'};
+%
% if ( exists($opt{'redirect'}) && $opt{'redirect'}
% && scalar(@$rows) == 1 && $total == 1
% && $type ne 'html-print'
% $cgi->delete('maxrecords');
% $cgi->param('_dummy', 1);
- ( show <SELECT NAME="maxrecords" onChange="window.location = '<% "$self_url?". $cgi->query_string %>;maxrecords=' + this.options[this.selectedIndex].value;">
+ ( show <SELECT NAME="maxrecords" onChange="window.location = '<% $self_url %>;maxrecords=' + this.options[this.selectedIndex].value;">
% foreach my $max ( map { $_ * $confmax } qw( 1 5 10 25 ) ) {
<OPTION VALUE="<% $max %>" <% ( $maxrecords == $max ) ? 'SELECTED' : '' %>><% $max %></OPTION>
Download full results<BR>
% $cgi->param('_type', "$xlsname.xls" );
- as <A HREF="<% "$self_url?". $cgi->query_string %>">Excel spreadsheet</A><BR>
+ as <A HREF="<% $self_url %>">Excel spreadsheet</A><BR>
% $cgi->param('_type', 'csv');
- as <A HREF="<% "$self_url?". $cgi->query_string %>">CSV file</A><BR>
+ as <A HREF="<% $self_url %>">CSV file</A><BR>
% if ( defined($opt{xml_elements}) ) {
% $cgi->param('_type', 'xml');
- as <A HREF="<% "$self_url?". $cgi->query_string %>">XML file</A><BR>
+ as <A HREF="<% $self_url %>">XML file</A><BR>
% }
% $cgi->param('_type', 'html-print');
- as <A HREF="<% "$self_url?". $cgi->query_string %>">printable copy</A>
+ as <A HREF="<% $self_url %>">printable copy</A>
</TD>
% $cgi->param('_type', "html" );
my $maxrecords = $args{'maxrecords'};
my $offset = $args{'offset'};
my %opt = %{ $args{'opt'} };
-my $self_url = $opt{'url'} || $cgi->url('-path_info' => 1, '-full' =>1);
+my $self_url = $opt{'url'} || $cgi->self_url;
my $count_sth = dbh->prepare($opt{'count_query'})
or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
my $limit = '';
my($confmax, $maxrecords, $offset );
-unless ( $type =~ /^(csv|\w*.xls)$/) {
+if ( !$type =~ /^(csv|\w*.xls)$/) {
# html mode
unless (exists($opt{count_query}) && length($opt{count_query})) {
( $opt{count_query} = $opt{query} ) =~
+++ /dev/null
-<% include('/elements/header.html', "$classname Inventory Activity Report") %>
-<% include('/elements/table-grid.html') %>
- <TR>
-% my $TH = 'TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=1';
- <<%$TH%> WIDTH="10%" ALIGN="left">Day (<% time2str("%B %Y", $sdate) %>)</TH>
-% foreach my $day (0..$numdays-1) {
- <<%$TH%> WIDTH="2%" ALIGN="right"><% $day+1 %></TH>
-% }
- </TR>
-% for (my $r=0; $r < scalar(@rows); $r++) {
- <TR>
-% my $TD = 'TD CLASS="grid" BGCOLOR="'.($r % 2 ? '#ffffff' : '#eeeeee').'"';
- <<%$TD%>><% $labels[$r] %></TD>
-% for my $day (0..$numdays-1) {
- <<%$TD%> ALIGN="right"><% $rows[$r][$day] %></TD>
-% }
- </TR>
-% }
-</TABLE>
-
-<%init>
-use Date::Parse 'str2time';
-use Date::Format 'time2str';
-use Data::Dumper 'Dumper';
-
-my ($agentnum, $classnum, $month, $year, $sdate, $edate);
-$classnum = $cgi->param('classnum'); # may be empty
-$agentnum = $cgi->param('agentnum'); # may also be empty
-my $classname = '';
-if($classnum) {
- my $class = qsearchs('inventory_class', { classnum => $classnum });
- die "classnum $classnum not found!" if !$class;
- $classname = $class->classname . ' ';
-}
-
-$month = $cgi->param('_month') || time2str('%m', time);
-$year = $cgi->param('_year') || time2str('%Y', time);
-
-$sdate = str2time("$year-$month-01");
-$edate = str2time($year + ($month == 12 ? 1 : 0) .
- '-' .
- (($month + 1) % 12 || 12) .
- '-01');
-my $numdays = sprintf("%.0f",($edate-$sdate)/86400);
-my @days = (0..$numdays - 1);
-# Initialize each row with zeroes.
-my @labels = (
- 'Opening Balance',
- 'Quantity Received',
- 'Quantity Sold',
- 'Quantity Returned',
-);
-
-if($agentnum) {
- push @labels, 'Transfer In', 'Transfer Out';
-}
-push @labels, 'Closing Balance';
-
-my %agent = ('agentnum' => $agentnum) if $agentnum;
-my %class = ('classnum' => $classnum) if $classnum;
-
-my @rows = ( map {[ (0) x $numdays ]} @labels);
-my $opening_balance = scalar(
- qsearch('h_inventory_item',
- { 'svcnum' => '',
- %agent,
- %class },
- FS::h_inventory_item->sql_h_search($sdate) )
- ) || 0;
-
-foreach my $day (0..$numdays-1) {
- $rows[0][$day] = ($day == 0) ?
- $opening_balance :
- $rows[-1][$day-1];
-
- my %history;
- foreach my $action (qw(insert replace_new replace_old)) {
- $history{$action} = [
- qsearch({
- 'table' => 'h_inventory_item',
- 'hashref' => { 'history_action' => $action,
- %class },
- 'order_by' => 'ORDER BY itemnum, history_date',
- 'extra_sql' =>
- ' AND history_date >= '.($sdate + 86400*$day).
- ' AND history_date < ' .($sdate + 86400*($day+1)),
- } )
- ];
- }
- # Incoming items: simple, just count the inserts
- $rows[1][$day] = scalar(grep {!$agentnum or $_->agentnum == $agentnum}
- @{ $history{'insert'} });
-
- # Other item changes: trickier.
- # Notice the order_by parameter above.
- # Both lists are sorted by itemnum, then by date, so unless some villain has
- # been rapidly replacing the same record several times per second, the
- # replace_old and replace_new from the same operation will be in the same
- # position.
- while(my $h_new = shift @{ $history{'replace_new'} }) {
- my $h_old = shift @{ $history{'replace_old'} };
- die "history error" if !defined($h_old)
- or $h_old->itemnum != $h_new->itemnum;
- if(!$agentnum or $h_new->agentnum == $agentnum) {
- if(!$h_old->svcnum and $h_new->svcnum) {
- # item was put into service.
- $rows[2][$day]++;
- }
- elsif($h_old->svcnum and !$h_new->svcnum) {
- # item was taken out of service.
- $rows[3][$day]++;
- }
- }
- if($agentnum and $h_old->agentnum != $agentnum and $h_new->agentnum == $agentnum) {
- # item was transferred from another agent
- $rows[4][$day]++;
- }
- elsif($agentnum and $h_old->agentnum == $agentnum and $h_new->agentnum != $agentnum) {
- # item was transferred to another agent
- $rows[5][$day]++;
- }
- # Add other cases here.
- }
- # Closing balance
- $rows[-1][$day] = $rows[0][$day]
- + $rows[1][$day]
- - $rows[2][$day]
- + $rows[3][$day];
- if($agentnum) {
- $rows[-1][$day] += $rows[4][$day] - $rows[5][$day];
- }
-}
-
-</%init>
-
+++ /dev/null
-<% include('/elements/header.html', 'Inventory Activity Report') %>
-
-<FORM ACTION="h_inventory_item.html" METHOD="GET">
-<TABLE BGCOLOR="#cccccc" CELLSPACING="0">
- <TR>
- <TD ALIGN="right">Inventory class: </TD>
- <TD><% include('/elements/select-table.html',
- 'element_name' => 'classnum',
- 'table' => 'inventory_class',
- 'name_col' => 'classname',
- 'value' => '',
- 'empty_label' => '(all)') %></TD>
- </TR>
- <TR>
- <TD ALIGN="right">Time period: </TD>
- <TD><% include('/elements/select-month_year.html') %></TD>
- </TR>
- <% include('/elements/tr-select-agent.html') %>
-</TABLE>
-
-<BR>
-<INPUT TYPE="submit" VALUE="Get Report">
-</FORM>
-
-<%init>
-</%init>
<% include('/elements/header.html', 'Tax Report' ) %>
-<FORM NAME="newtax">
+<FORM ACTION="report_queued_newtax.cgi" METHOD="GET">
<TABLE>
</TABLE>
-<BR><INPUT TYPE="button" NAME='fetch' VALUE="Get Report" onClick="document.newtax.fetch.disabled=true; process();">
+<BR><INPUT TYPE="submit" VALUE="Get Report">
</FORM>
-<% include( '/elements/progress-init.html',
- 'newtax',
- [ qw( agentnum beginning ending ) ],
- 'report_queued_newtax.cgi',
- )
-%>
-
<% include('/elements/footer.html') %>
<%init>
#here is the agent virtualization
push @where, $curuser->agentnums_sql( 'table'=>'cust_main' );
+#well, because cust_bill_pkg.cgi has it and without it the numbers don't match..
+push @where , " payby != 'COMP' "
+ unless $cgi->param('include_comp_cust');
+
my %total = ();
my %total_legacy = ();
foreach my $agentnum (@agentnums) {
-<% $server->process %>
+<% include("/elements/header.html", "Queue Tax Report") %>
+<% include("/elements/error.html") %>
+% unless ($error) {
+ <CENTER>
+ Report queued. Check the job queue for status.
+ </CENTER>
+% }
+<% include("/elements/footer.html") %>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $server =
- new FS::UI::Web::JSRPC 'FS::tax_rate::queue_liability_report', $cgi;
+my $error = FS::tax_rate::queue_liability_report($cgi);
</%init>
# )
sub balance {
- my($start, $end, $cutoff) = @_; #, %opt ?
+ my($start, $end, $offset) = @_; #, %opt ?
+ #handle start and end ranges (86400 = 24h * 60m * 60s)
+ my $str2time = str2time_sql;
+ my $closing = str2time_sql_closing;
+
+ # $end == 0 means "+infinity", while $start == 0 really means 0
+ # so we should always include a start condition
+ $start = "( $str2time now() $closing - ". ($start + $offset) * 86400 . ' )';
+ # but only include an end condition if $end != 0
+ $end = $end ?
+ "( $str2time now() $closing - ". ($end + $offset) * 86400 . ' )'
+ : '';
+
+ #$opt{'unapplied_date'} = 1;
+
+ FS::cust_main->balance_date_sql( $start, $end, 'unapplied_date'=>1,
+ 'cutoff' => "( $str2time now() $closing - ".$offset * 86400 . ')' );
- FS::cust_main->balance_date_sql( $start, $end,
- 'cutoff' => $cutoff,
- 'unapplied_date'=>1,
- );
}
</%once>
<% include ( '/elements/tr-select-otaker.html' ) %>
- <% include ( '/elements/tr-input-text.html',
- 'label' => 'Ticket #',
- 'field' => 'ticketid',
- )
- %>
-
- <TR>
- <TD>Account</TD>
- <TD>
- <SELECT NAME="svcnum">
- <OPTION VALUE="">(all)
-% foreach my $svc_acct (@svc_acct) {
- <OPTION VALUE="<% $svc_acct->svcnum %>"><% $svc_acct->username %></OPTION>
-% }
- </SELECT>
- </TD>
- </TR>
-
</TABLE>
<BR>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
-my $conf = new FS::Conf;
-
-my @pkgparts = $conf->config('support_packages');
-
-my @svc_acct = ();
-if ( @pkgparts ) {
- @svc_acct = qsearch({
- 'table' => 'svc_acct',
- 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '.
- ' LEFT JOIN cust_pkg USING ( pkgnum ) ',
- 'extra_sql' => 'WHERE pkgpart IN ('. join(',', @pkgparts). ')',
- });
-}
-
</%init>
'name_singular' => 'transaction',
'query' => $query,
'count_query' => $count_query,
- 'count_addl' => [ $format_seconds_sub, $format_seconds_sub, ],
+ 'count_addl' => [ $format_seconds_sub, ],
'header' => [ 'Ticket #',
'Ticket',
'Date',
'Time',
- 'Applied',
],
'fields' => [ 'ticketid',
sub { encode_entities(shift->get('subject')) },
sub { my $seconds = shift->get('transaction_time');
&{ $format_seconds_sub }( $seconds );
},
- sub { my $seconds = shift->get('support');
- &{ $format_seconds_sub }( $seconds );
- },
],
'links' => [
$link,
$link,
'',
'',
- '',
],
)
%>
my $format_seconds_sub = sub {
my $seconds = shift;
- #(($seconds < 0) ? '-' : '') . concise(duration($seconds));
- (($seconds < 0) ? '-' : '' ). int(abs($seconds)/3600)."h".sprintf("%02d",(abs(
-$seconds)%3600)/60)."m";
+ (($seconds < 0) ? '-' : '') . concise(duration($seconds));
};
</%once>
";
my $join = 'JOIN Tickets ON Transactions.ObjectId = Tickets.Id '.
- 'JOIN Users ON Transactions.Creator = Users.Id '.
- 'LEFT JOIN acct_rt_transaction '.
- ' ON Transactions.Id = acct_rt_transaction.transaction_id';
+ 'JOIN Users ON Transactions.Creator = Users.Id ';
+
my $where = "
WHERE objecttype='RT::Ticket'
AND ( ( Transactions.Type = 'Set'
$where .= " AND Users.name = '$1' ";
}
-if ( $cgi->param('ticketid') =~ /^\s*(\d+)\s*$/ ) {
- $where .= " AND Tickets.ID = $1";
-}
-
-if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
- $where .= " AND acct_rt_transaction.svcnum = $1";
-}
-
my $query = {
- 'select' => "Transactions.*, Tickets.Id AS ticketid, Tickets.Subject, Users.name as otaker, $transactiontime AS transaction_time, acct_rt_transaction.support",
+ 'select' => "Transactions.*, Tickets.Id AS ticketid, Tickets.Subject, Users.name as otaker, $transactiontime AS transaction_time",
#'table' => 'Transactions',
'table' => 'transactions',
- 'addl_from' => $join,
+ 'addl_from' => $join.
+ 'LEFT JOIN acct_rt_transaction '.
+ ' ON Transactions.Id = acct_rt_transaction.transaction_id',
'extra_sql' => $where,
'order by' => 'ORDER BY Created',
};
my $count_query =
- "SELECT COUNT(*), SUM($transactiontime), SUM(acct_rt_transaction.support) FROM Transactions $join $where";
+ "SELECT COUNT(*), SUM($transactiontime) FROM Transactions $join $where";
-my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('ticketid'); } ];
+my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('id'); } ];
</%init>
<% include( 'elements/search.html',
'title' => 'Query Results',
'name' => 'rows',
- 'query' => "SELECT $sql",
- )
+ 'query' => 'SELECT '. ( $cgi->param('sql')
+ || errorpage('Empty query') ),
+ )
%>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Raw SQL');
-my $sql = $cgi->param('sql') or errorpage('Empty query');
-$sql =~ s/;+\s*$//; #remove trailing ;
-
</%init>
<%once>
sub unapplied_payments {
- my($start, $end, $cutoff) = @_;
+ my($start, $end, $offset) = @_;
+
+ #handle start and end ranges (86400 = 24h * 60m * 60s)
+ my $str2time = str2time_sql;
+ my $closing = str2time_sql_closing;
+ $start = "( $str2time now() $closing - ".($start + $offset) * 86400 . ' )';
+ $end = $end ?
+ "( $str2time now() $closing - ".($end + $offset) * 86400 . ' )'
+ : '';
+
+ FS::cust_main->unapplied_payments_date_sql( $start, $end );
- FS::cust_main->unapplied_payments_date_sql( $start, $end,
- 'cutoff' => $cutoff,
- );
}
</%once>
+++ /dev/null
-% if($magic eq 'print') {
-<% include('/elements/header.html', "Download Batch") %>
-<FORM NAME="OneTrueForm">
-<INPUT TYPE="hidden" NAME="batchnum" VALUE="<% $batchnum %>">
-% $cgi->delete('magic');
-<% include('/elements/progress-init.html',
- 'OneTrueForm',
- [ 'batchnum' ],
- $p.'misc/process/bill_batch-print.html',
- {'url' => $cgi->self_url . ';magic=download'},
- '',
-) %></FORM>
-<SCRIPT TYPE="text/javascript">process();</SCRIPT>
-<% include('/elements/footer.html') %>
-% }
-%
-% elsif($magic eq 'download') {
-% $m->clear_buffer;
-% $r->content_type('application/pdf');
-% $r->headers_out->add('Content-Disposition' => 'attachment;filename="invoice_batch_'.$batchnum.'.pdf"');
-<% $batch->pdf %>
-% $batch->pdf('');
-% my $error = $batch->replace;
-% warn "error deleting cached PDF: '$error'\n" if $error;
-% }
-% else {
-<% include('/search/elements/search.html',
- 'title' => $close ?
- "Batch $batchnum closed." :
- "Invoice Batch $batchnum",
- 'name' => 'invoices',
- 'query' => { 'table' => 'cust_bill_batch',
- 'select' => join(', ',
- 'cust_bill.*',
- FS::UI::Web::cust_sql_fields(),
- 'cust_main.custnum AS cust_main_custnum',
- ),
- 'hashref' => { },
- 'addl_from' =>
- 'LEFT JOIN cust_bill USING ( invnum ) '.
- 'LEFT JOIN cust_main USING ( custnum )',
- 'extra_sql' => '',
- " WHERE batchnum = $batchnum",
- },
- 'count_query' => "SELECT COUNT(*) FROM cust_bill_batch WHERE batchnum = $batchnum",
- 'html_init' => $html_init,
- 'header' => [ 'Invoice #',
- 'Amount',
- 'Date',
- 'Customer',
- ],
- 'fields' => [ sub { shift->cust_bill->display_invnum },
- sub { sprintf($money_char.'%.2f',
- shift->cust_bill->charged ) },
- sub { time2str('%b %d %Y',
- shift->cust_bill->_date ) },
- sub { shift->cust_bill->cust_main->name },
- ],
- 'align' => 'rrll',
- 'links' => [ ($link) x 3, $clink,
- ],
- 'really_disable_download' => 1,
-) %>
-% }
-<%init>
-
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
-
-my $conf = new FS::Conf;
-my $batch;
-my $batchnum = $cgi->param('batchnum');
-
-$batch = FS::bill_batch->by_key($batchnum);
-die "Batch '$batchnum' not found!\n" if !$batch;
-
-my $magic = $cgi->param('magic');
-my $html_init = '';
-
-my $close = $cgi->param('close');
-$batch->close if $close;
-
-if(!$magic) {
- $cgi->param('magic' => 'print');
- $cgi->delete('close');
- $html_init = '<A HREF="'.$cgi->self_url.'">Download this batch</A><BR>';
- if($batch->status eq 'O') {
- $cgi->param('close' => 1);
- $cgi->delete('magic');
- $html_init .= '<A HREF="'.$cgi->self_url.'">Close this batch</A><BR>';
- }
- $html_init .= '<BR>';
-}
-
-my $link = [ "$p/view/cust_bill.cgi?", 'invnum' ];
-my $clink = [ "$p/view/cust_main.cgi?", 'custnum' ];
-my $money_char = $conf->config('money_char') || '$';
-
-</%init>
})
%>
<BR>
-% my @part_tag = $cust_main->part_tag;
-% if ( $conf->config('cust_tag-location') eq 'top' && @part_tag ) {
-<TABLE STYLE="margin-bottom:8px" CELLSPACING=2>
-% foreach my $part_tag ( @part_tag ) {
-<TR>
- <TD>
- <FONT SIZE="+1"
- <% length($part_tag->tagcolor)
- ? 'STYLE="background-color:#'.$part_tag->tagcolor.'"'
- : ''
- %>><% $part_tag->tagname.': '. $part_tag->tagdesc |h %></FONT>
- </TD>
-</TR>
-% }
-</TABLE>
-% }
<% include('/elements/menubar.html',
{ 'newstyle' => 1,
%views,
)
%>
-<DIV CLASS="fstabcontainer">
+<BR>
<% include('/elements/init_overlib.html') %>
This customer's signup URL: <A HREF="<% $signupurl %>?ref=<% $custnum %>"><% $signupurl %>?ref=<% $custnum %></A><BR><BR>
% }
-%if ( $conf->exists('maestro-status_test') ) {
- <A HREF="<% $p %>misc/maestro-customer_status-test.html?<% $custnum %>">Test maestro status</A><BR><BR>
-% }
<A NAME="cust_main"></A>
<TABLE BORDER=0>
<% include('cust_main/change_history.html', $cust_main ) %>
% }
-</DIV>
<% include('/elements/footer.html') %>
<%init>
%if ( $FS::CurrentUser::CurrentUser->access_right('Bill customer now')
% && ! $cust_main->is_encrypted($cust_main->payinfo)
% ) {
-%# (<A HREF="<% $p %>misc/bill.cgi?<% $cust_main->custnum %>">Bill now</A>)
- (<% include('/elements/bill.html',
- custnum => $cust_main->custnum,
- label => 'Bill now',
- url => $p.'view/cust_main.cgi?'.$cust_main->custnum,
- ) %>)
+ (<A HREF="<% $p %>misc/bill.cgi?<% $cust_main->custnum %>">Bill now</A>)
% }
<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
<TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_main->statuscolor %>"><B><% ucfirst($cust_main->status) %></B></FONT></TD>
</TR>
-% my @part_tag = $cust_main->part_tag;
-% if ( $conf->config('cust_tag-location') =~ /^(cust_misc|)$/ && @part_tag ) {
-<TR>
- <TD ALIGN="right">Tags</TD>
- <TD BGCOLOR="#ffffff">
-% foreach my $part_tag ( @part_tag ) {
- <FONT <% length($part_tag->tagcolor)
- ? 'STYLE="background-color:#'.$part_tag->tagcolor.'"'
- : '' %>
- ><% $part_tag->tagname.': '. $part_tag->tagdesc |h %></FONT>
- <BR>
-% }
- </TD>
-</TR>
-% }
-
-%unless ( scalar(@agentnums) == 1
-% && !$curuser->access_right('View customers of all agents') ) {
-% my $agent = qsearchs('agent',{ 'agentnum' => $cust_main->agentnum } );
+%my $agent;
+%if ( $num_agents == 1 ) {
+% my @agents = qsearchs( 'agent', {} );
+% $agent = $agents[0];
+%} else {
+% $agent = qsearchs('agent',{ 'agentnum' => $cust_main->agentnum } );
<TR>
<TD ALIGN="right">Agent</TD>
<TD BGCOLOR="#ffffff"><% $agent->agentnum %>: <% $agent->agent %></TD>
my $conf = new FS::Conf;
my $date_format = ($conf->config('date_format') || "%m/%d/%Y");
-my $curuser = $FS::CurrentUser::CurrentUser;
-
-my @agentnums = $curuser->agentnums;
+my $sth = dbh->prepare('SELECT COUNT(*) FROM agent') or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my $num_agents = $sth->fetchrow_arrayref->[0];
</%init>
#for services.html
'svc_external-skip_manual' => $conf->exists('svc_external-skip_manual'),
'legacy_link' => $conf->exists('legacy_link'),
- 'svc_broadband-manage_link' => scalar($conf->config('svc_broadband-manage_link')),
- 'maestro-status_test' => $conf->exists('maestro-status_test'),
+ 'svc_broadband-manage_link' => $conf->config('svc_broadband-manage_link'),
);
#subroutines
% if ( $curuser->access_right('Unprovision customer service') ) {
<FONT SIZE="-2">( <%svc_unprovision_link($cust_svc)%> )</FONT>
% }
-
-% if ( $part_svc->svcdb eq 'svc_pbx' && $opt{'maestro-status_test'} ){
- <FONT SIZE="-2">( <A HREF="<% $p %>misc/maestro-customer_status-test.html?<% $cust_pkg->custnum.'+'.$cust_svc->svcnum %>">Test maestro status</A> )</FONT>
-% }
-
</TD>
</TR>
% }
-<FORM METHOD="GET" NAME="CreateTicketForm" STYLE="display:inline">
-<SCRIPT TYPE="text/javascript">
-function updateTicketLink() {
- var link = document.getElementById('CreateTicketLink');
- var selector = document.getElementById('Queue')
- link.href = "<% $new_base.'?'.
- join(';', map(
- { ($_ eq 'Queue') ? () : "$_=$new_param{$_}"}
- keys %new_param),'Queue=') %>" + selector.options[selector.selectedIndex].value;
-}
-</SCRIPT>
-<A id="CreateTicketLink" HREF="<% $new_link %>">Create new ticket</A>
- in queue
+<FORM METHOD="GET" ACTION="<% $new_base %>" NAME="CreateTicketForm">
+<INPUT TYPE="submit" VALUE="Create new ticket">
+in queue
+<SELECT NAME="Queue">
% my %queues = FS::TicketSystem->queues();
-% if( $conf->exists('ticket_system-force_default_queueid') ) {
-<B><% $queues{$new_param{'Queue'}} %></B>
-<INPUT TYPE="hidden" NAME="Queue" VALUE="<% $new_param{'Queue'} %>">
-% }
-% else {
-<SELECT NAME="Queue" id="Queue" onchange="updateTicketLink()">
-% foreach my $queueid ( sort { $queues{$a} cmp $queues{$b} } keys %queues ) {
+% foreach my $queueid ( keys %queues ) {
% #should consider whether the user has ACL to create ticket in each queue
<OPTION VALUE="<% $queueid %>"
<% $queueid == $new_param{'Queue'} ? 'SELECTED' : '' %>
><% $queues{$queueid} |h %>
% }
</SELECT>
-<SCRIPT DEFER TYPE="text/javascript">updateTicketLink();</SCRIPT>
+% foreach my $param ( grep { $_ ne 'Queue' } keys %new_param ) {
+ <INPUT TYPE="hidden" NAME="<% $param %>" VALUE="<% $new_param{$param} |h %>">
% }
</FORM>
<BR>
<%init>
-my( $conf ) = new FS::Conf;
my( $cust_main ) = @_;
my( @tickets ) = $cust_main->tickets;
<TD BGCOLOR="#FFFFFF"><B><% $prospectnum %></B></TD>
</TR>
-%unless ( scalar(@agentnums) == 1
-% && !$curuser->access_right('View customers of all agents') ) {
+%unless ( scalar(@agentnums) == 1 ) {
% my $agent = qsearchs('agent',{ 'agentnum' => $prospect_main->agentnum } );
<TR>
<TD ALIGN="right">Agent</TD>
<% include('/view/elements/tr.html', label=>'Send read receipts',
value=>$svc_acct->cgp_sendmdnmode ) %>
-%# vacation message
-%#XXX finish me... do we need to search for specific rules
-%# (and hide them?) need to see what CGP gives back after we've added a rule
- <% include('/elements/init_overlib.html') %>
-
- <TR>
- <TD ALIGN="right">Vacation message</TD>
- <TD BGCOLOR="#FFFFFF">
- <% include('/elements/popup_link.html',
- 'action' => $p.'edit/cgp_rule-vacation.html?'.
- 'svcnum='. $svc_acct->svcnum,
- 'label' => '(add)', #XXX (edit)
- 'actionlabel' => 'Vacation message',
- 'width' => 600,
- 'height' => 300,
- #'color'
- )
- %>
- </TD>
- </TR>
-
-%# redirect all mail
-%#XXX finish me...
-
- <TR>
- <TD ALIGN="right">Redirect all mail</TD>
- <TD BGCOLOR="#FFFFFF">
- <% include('/elements/popup_link.html',
- 'action' => $p.'edit/cgp_rule-redirect_all.html?'.
- 'svcnum='. $svc_acct->svcnum,
- 'label' => '(add)', #XXX (edit)
- 'actionlabel' => 'Redirect all mail',
- 'width' => 763,
- #'height'
- #'color'
- )
- %>
- </TD>
- </TR>
+%#XXX vacation message, redirect all mail
%# mail rules
%>
<% include('/view/elements/tr.html',
- label=>'Files limit',
+ label=>'Files limt',
value=>$svc_domain->acct_def_file_maxnum,
)
%>
<FORM NAME="SlaveForm" METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
- Or
% if ( @records ) {
- delete all records and
+ Delete all records and
% }
- slave from nameserver IP
+ Or slave from nameserver IP
<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
<INPUT TYPE="hidden" NAME="reczone" VALUE="@">
<INPUT TYPE="hidden" NAME="recaf" VALUE="IN">
</TABLE>
<BR>
-
-<% include('elements/svc_export_settings.html', $svc_forward) %>
-
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>
echo "done."
done
- echo -n "Starting freeside-selfservice-xmlrpcd: "
- freeside-selfservice-xmlrpcd $SELFSERVICE_USER
- echo "done."
-
#ip=`/sbin/ifconfig $IF | grep 'inet addr:' | cut -d: -f2- | cut -d' ' -f1`
#cp /opt/rt3/etc/RT_SiteConfig.pm.ORIG /opt/rt3/etc/RT_SiteConfig.pm
#perl -pi -e "s/localhost/$ip/" /opt/rt3/etc/RT_SiteConfig.pm
fi
done
- if [ -e /var/run/freeside/selfservice-xmlrpcd.pid ]; then
- echo -n "Stopping freeside-selfservice-xmlrpcd: "
- kill `cat /var/run/freeside/selfservice-xmlrpcd.pid`
- echo "done."
- fi
-
;;
restart)
+++ /dev/null
-# its more notes than a script, so no #!/bin/sh yet
-
-# s/ivan/username/ in buildsysrc and below
-
-
-###
-# host dirs
-###
-
-
-cd
-mkdir public_html
-mkdir mock
-mkdir redhat
-mkdir redhat/SRPMS
-mkdir buildsys
-mkdir buildsys/ref
-mkdir buildsys/ref/SRPMS
-
-cd buildsys
-for a in build-freeside buildsysrc cvs-check-and-build enrpm expect-addsign expect-signrepo ovid2flute refresh-repo; do
-ln -s ~/freeside/rpm/build/$a .
-done
-
-
-###
-# vserver setup
-###
-
-
-#ftp://ftp.pld-linux.org/people/hawk/vserver-templates/Centos
-#sudo vserver centos5 build -m template --context 5 --hostname centos5.freeside.biz --interface dummy0:10.5.4.5/24 -- -d centos5 -t centos5-i686.tar.bz2
-sudo vserver centos5 build -m template --context 5 --hostname centos5.freeside.biz --interface dummy0:10.5.4.5/24 -- -d centos5 -t centos5-x86_64.tar.bz2
-
-#something like this as /etc/init.d/vserver-nat:
-!/bin/sh
-iptables -t nat -F
-iptables -t nat -A POSTROUTING -s 10.5.4.0/24 -d ! 10.5.4.0/24 -j SNAT --to-source 192.168.1.143
-
-vserver centos5 start
-vserver centos5 enter
-#edit /etc/resolv.conf (easier from outside, no vi inside yet)
-
-yum update
-yum install openssh-server vim-minimal zsh screen sudo perl patch cvs diffutils rpm-build rsync
-
-adduser ivan #username
-cd ~ivan #username
-mkdir .ssh
-vi .ssh/authorized_keys
-
-mkdir redhat
-mkdir redhat/BUILD
-mkdir redhat/RPMS
-mkdir redhat/SOURCES
-mkdir redhat/SPECS
-mkdir redhat/SRPMS
-chown -R ivan:ivan redhat
-
-vi ~/.rpmmacros
-%_gpg_path /home/ivan/.gnupg
-%_gpg_name Freeside Internet Services, Inc. RPM Signing Key
-
-vi /etc/ssh/sshd_config #ListenAddress
-#also need to edit on the host so the vserver can claim its address
-
-vi /etc/pam.d/sshd
-#comment out:
-#session required pam_loginuid.so
-
-/etc/init.d/sshd restart
-
-vi /etc/sudoers
-
-exit #and test ssh'ing in
-
-###
-# more...
-###
-
-#copy the stuff from rpm/build/native into /home/ivan (#username) in the vserver
-cd rpm/build/native
-for a in build-from-cvs freeside-cvs makesrpm ovid2flute ovid-0.12-1.x86_64.rpm Ovid.diff; do
- cp $a /var/lib/vservers/centos5/home/ivan/
-done
-
-for a in build-from-cvs freeside-cvs makesrpm ovid2flute; do
- chmod a+rx /var/lib/vservers/centos5/home/ivan/$a
-done
-
-vserver centos5 enter #or ssh 10.5.4.5 and sudo
-
-rpm -i ovid-0.12-1.x86_64.rpm
-cd /usr/lib/perl5/site_perl/5.*/Ovid
-patch < ~ivan/Ovid.diff
-
-#also checkout the necessary freeside versions...
-export CVSROOT=":pserver:anonymous:anonymous@cvs.freeside.biz:/home/cvs/cvsroot"
-
-cvs checkout -rFREESIDE_1_7_BRANCH -d freeside-1.7 freeside
-#cvs checkout -rFREESIDE_1_9_BRANCH -d freeside-1.9 freeside
-
-###
-# yet more
-###
-
-cp -i /var/lib/vservers/centos5/etc/yum.repos.d/CentOS-Base.repo /etc/yum/repos.d/
-#and s/$releasever/5/g;
-
-cp expect-* /usr/local/bin/
-#edit them and set your real passphrase for the gpg key you're using
-#(as per that dir above)
-
-###
-# and the repository
-###
-
-cd
-mkdir -p public_html/repo/centos/5/freeside-1.7/testing/x86_64/
-mkdir public_html/repo/centos/5/freeside-1.7/testing/i686
-
-###
-# and for enrpm!
-###
-
-vserver centos5 enter
-yum install perl-libwww-perl make
-cpan
-install RPM::Specfile
-#if it fails, might need to go to /root/.cpan/build/RPM-Specfile-* and do it manually
-install YAML
-
-#for user cpan-ability
-mkdir .cpan
-mkdir .cpan/CPAN
-cp /usr/lib/perl5/5.8.8/CPAN/Config.pm .cpan/CPAN/MyConfig.pm
-vi .cpan/CPAN/MyConfig.pm #and just leave and change the /root ones
-chmod a+rx .cpan/CPAN/MyConfig.pm
-
-#edit ovid2flue and set user
-
-###
-# references
-###
-
-http://www.freeside.biz/mediawiki/index.php/Freeside:Documentation:CreatingRPMRepo
+++ /dev/null
-#!/bin/sh
-#
-# Copyright 2008, Elirion, Inc. All rights reserved.
-# This software is licensed under the same terms as Freeside itself.
-#
-# This script rebuilds SRPMs of Freeside builds or the required Perl modules on all the target
-# distributions and versions using mock. After a successful build, it signs the resulting RPMs
-# and scp's them to the server where the yum repositories are hosted.
-# (Of course, koji is supposed to do all this, including updating the repo.)
-
-VERSIONS='1.7 1.9'
-#VERSIONS='1.7 1.9 2.1'
-REPO=testing
-BRANCH=
-DISTROS='centos sles'
-CENTOSVERS='5'
-SLESVERS=10
-WHICHVERS=
-ARCHS='i386 x86_64'
-
-BUILDSYSDIR=`dirname $0`
-
-MOCKWORK="$BUILDSYSDIR/mockwork"
-
-#MOCKARGS='--autocache'
-MOCKARGS="--configdir=$BUILDSYSDIR/mock --resultdir=$MOCKWORK"
-
-if [ -f $BUILDSYSDIR/buildsysrc ]; then
- #chmod a+x $BUILDSYSDIR/buildsysrc
- #echo $BUILDSYSDIR/buildsysrc
- . $BUILDSYSDIR/buildsysrc
-fi
-if [ -f $HOME/buildsysrc ]; then
- #chmod a+x $HOME/buildsysrc
- #echo $HOME/buildsysrc
- . $HOME/buildsysrc
-fi
-
-EXPECT_ADDSIGN=$BUILDSYSDIR/expect-addsign
-if [ -f /usr/local/bin/expect-addsign ]; then
- EXPECT_ADDSIGN=/usr/local/bin/expect-addsign
-fi
-
-usage() {
- echo "build-freeside: build RPMs for all target distros and architectures using mock"
- echo "where:"
- echo " -a <archs>: change architectures (currently: $ARCHS)"
- echo " -b <branch>: change branch (currently: $BRANCH)"
- echo " -d <distros>: change distributions (currently: $DISTROS)"
- echo " -m <arguments>: change arguments passed to 'mock' (currently: $MOCKARGS)"
- echo " -r <repo>: change repositories (currently: $REPO)"
- echo " -s <srpms>: build these SRPMs instead of new ones in staging area"
- echo " -v <versions>: change versions (currently: $VERSIONS)"
- echo " -w <distvers>: change distro version (currently: $WHICHVERS)"
- exit 0
-}
-
-while getopts "a:b:d:hm:r:s:v:w:" flag
-do
- case $flag in
- a)
- echo "Changing architectures from $ARCHS to $OPTARG"
- ARCHS=$OPTARG;;
- b)
- echo "Changing branch from $BRANCH to $OPTARG"
- BRANCH=$OPTARG;;
- d)
- echo "Changing distros from $DISTROS to $OPTARG"
- DISTROS=$OPTARG;;
- m)
- echo "Changing mock arguments from $MOCKARGS to $OPTARG"
- MOCKARGS=$OPTARG;;
- r)
- echo "Changing repository from $REPO to $OPTARG"
- REPO=$OPTARG;;
- s)
- echo "Changing SRPMS from $SRPMS to $OPTARG"
- SRPMS=$OPTARG;;
- v)
- echo "Changing versions from $VERSIONS to $OPTARG"
- VERSIONS=$OPTARG;;
- w)
- echo "Changing which distro versions from $WHICHVERS to $OPTARG"
- WHICHVERS=$OPTARG;;
- *)
- usage;;
- esac
-done
-
-if [ "${SRCFOLDER}x" = "x" ]; then
- PWD=`pwd`
- echo "No source folder defined! (BUILDSYSDIR=$BUILDSYSDIR pwd=$PWD)"
- exit
-fi
-
-if [ "${REFFOLDER}x" = "x" ]; then
- echo "No reference folder defined!"
- exit
-fi
-
-if [ "${SRPMS}x" = "x" ]; then
- # Work out the new SRPMs on grosbeak
- SRPMS=`/usr/bin/rsync -Cavz --dry-run $SRCFOLDER/ $REFFOLDER | grep .src.rpm | grep -v safecat | tr '\n' ' '`
-
- # Go and get the SRPMs
- /usr/bin/rsync -Cavz $SRCFOLDER/ $REFFOLDER
-fi
-
-# Make sure the SRPMs are there
-for srpm in ${SRPMS}
-do
- if [ ! -f $REFFOLDER/${srpm} ]
- then
- echo "No such file: $REFFOLDER/${srpm}"
- exit
- fi
-done
-
-# Build all the SRPMs
-for srpm in ${SRPMS}
-do
- for distro in $DISTROS
- do
- if [ "${WHICHVERS}x" = "x" ]; then
- if [ "$distro" = "centos" ]; then
- DISTVERS=$CENTOSVERS
- fi
- if [ "$distro" = "sles" ]; then
- DISTVERS=$SLESVERS
- fi
- else
- DISTVERS=$WHICHVERS
- fi
- for distver in $DISTVERS
- do
- os=${distro}-${distver}
- for arch in $ARCHS
- do
- echo "$os - $arch: $srpm"
- echo mock $MOCKARGS -r ${os}-${arch} $REFFOLDER/${srpm}
- time mock $MOCKARGS -r ${os}-${arch} $REFFOLDER/${srpm}
- if true #[ -f $MOCKWORK/${os}-${arch}/state/status ] && grep done $MOCKWORK/${os}-${arch}/state/status
- then
- for VERSION in $VERSIONS
- do
- DEST=$VERSION
- if [ "${BRANCH}x" != "x" ]
- then
- DEST=$BRANCH
- fi
- # Copy freeside RPMs for this version only
- #FILES=`ls -1 $MOCKWORK/${os}-${arch}/result/freeside*-${VERSION}-*.rpm | grep -v .src.rpm | tr '\n' ' '`
- FILES=`ls -1 $MOCKWORK/freeside*-${VERSION}-*.rpm | grep -v .src.rpm | tr '\n' ' '`
- echo $FILES
- if [ "${FILES}x" != "x" ]
- then
- for FILE in $FILES
- do
- $EXPECT_ADDSIGN $FILE
- done
- if [ "${REPOMACHINE}x" != "x" ]
- then
- scp -p $FILES $REPOUSER@$REPOMACHINE:$REPOFOLDER/repo/${distro}/${distver}/freeside-${DEST}/${REPO}/${arch}
- else
- cp -p $FILES $REPOFOLDER/repo/${distro}/${distver}/freeside-${DEST}/${REPO}/${arch}
- fi
- fi
- # Copy non-freeside RPMs to all versions
- #FILES=`ls -1 $MOCKWORK/${os}-${arch}/result/*.rpm | grep -v freeside | grep -v .src.rpm | tr '\n' ' '`
- FILES=`ls -1 $MOCKWORK/*.rpm | grep -v freeside | grep -v .src.rpm | tr '\n' ' '`
- echo $FILES
- if [ "${FILES}x" != "x" ]
- then
- for FILE in $FILES
- do
- $EXPECT_ADDSIGN $FILE
- done
- if [ "${REPOMACHINE}x" != "x" ]
- then
- scp -p $FILES $REPOUSER@$REPOMACHINE:$REPOFOLDER/repo/${distro}/${distver}/freeside-${DEST}/${REPO}/${arch}
- else
- cp -p $FILES $REPOFOLDER/repo/${distro}/${distver}/freeside-${DEST}/${REPO}/${arch}
- fi
- fi
- done
- fi
- done
- done
- done
-done
-
-echo "build-freeside done"
+++ /dev/null
-# Define shell variables for the Freeside RPM build system
-#
-
-SRCFOLDER=ivan@10.5.4.5:/home/ivan/redhat/SRPMS
-REFFOLDER=$HOME/buildsys/ref/SRPMS
-#ARCHS='i386 x86_64'
-REPOFOLDER=/home/ivan/public_html
-REPOBASEFOLDER=/home/ivan/public_html
-KEYID=rpm
-
-VERSIONS='1.7'
-DISTROS='centos'
-CENTOSVERS='5'
-#ARCHS='x86_64'
+++ /dev/null
-#!/bin/sh
-#
-# Copyright 2009, Elirion, Inc. All rights reserved.
-# This software is licensed under the same terms as Freeside itself.
-#
-# This script wraps other scripts in the build system. It does a CVS comparison on the vserver
-# to determine if the CVS contents have changed. If so, an SRPM is built. The script then invokes
-# the local build script which pulls down this SRPM and uses mock to build binary RPMs for the
-# default targets. Finally, the repository is updated.
-#
-# There's currently no testing for failure.
-
-FORCE_FLAG=
-QUIET_FLAG=
-
-usage() {
- echo "cvs-check-and-build: check Freeside CVS and build RPMs if changed"
- echo "where:"
- echo " -f: force SRPM rebuild even if CVS contents have not changed"
- echo " -h: print this help message"
- echo " -q: run yum-arch and createrepo in quiet mode"
- exit 0
-}
-
-while getopts "fhq" flag
-do
- case $flag in
- f)
- echo "Force mode"
- FORCE_FLAG=-f;;
- q)
- echo "Quiet mode"
- QUIET_FLAG=-q;;
- *)
- usage;;
- esac
-done
-
-#ssh 10.5.4.5 /home/rsiddall/build-from-cvs $FORCE_FLAG
-#cd /home/rsiddall/buildsys; ./build-freeside; ./refresh-repo $QUIET_FLAG
-
-ssh 10.5.4.5 ./build-from-cvs $FORCE_FLAG
-
-cd ~/buildsys; ./build-freeside; ./refresh-repo $QUIET_FLAG
-
+++ /dev/null
-#!/usr/bin/perl -w
-#
-# Attempt to build RPMs for Freeside from a tarball or module
-#
-# Contains portions of "cpanspec" http://cpanspec.sourceforge.net/
-
-=head1 NAME
-
-enrpm - Attempt to build RPMs for Freeside from a tarball or a module
-
-=head1 SYNOPSIS
-
-enrpm [options] [tarball|module-name [...]]
-
- Options:
- --help -h Help message
-
-=head1 DESCRIPTION
-
-B<enrpm> will generate a spec file to build a rpm from a CPAN-style
-Perl module distribution, then try to build RPMs from that spec file
-under mock, and update the repositories if successful.
-
-B<enrpm> uses the file extension of the tarball or module-name specified
-on the command line to determine if the argument is a tarball or a module name.
-
-Modules are downloaded from the CPAN. You must have initialized CPAN on the machine
-for B<enrpm> to be able to do this.
-
-For tarballs B<enrpm> attempts to build a usable .spec file using cpanflute2, and then builds
-an SRPM from the .spec file and the tarball.
-
-Once an SRPM has been built, B<enrpm> uses an external script to build binary RPMs under "mock".
-It then uses a second script to update the repositories.
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<--help>
-
-Print a brief help message and exit.
-
-=item B<--packager>
-
-Specify the name and e-mail address of the packager. This winds up in auto-generated .spec files.
-
-=item B<--server>
-
-Specify the host name or IP address of the server on which the SRPM will be built. This server must be
-set up for SRPM building, which means it must have both B<ovid> and B<cpanflute2> installed on it.
-
-=item B<--dry-run>
-
-Just print out commands, don't actually run them.
-
-=back
-
-=head1 BUGS
-
-Currently has little in the way of error detection and diagnostics.
-
-The back-end should be replaced with an existing build system such as koji.
-
-=head1 AUTHOR
-
-Richard Siddall <richard.siddall@elirion.net>
-
-=head1 SEE ALSO
-
-L<perl(1)>, L<cpan2rpm(1)>, L<cpanflute2(1)>
-
-=cut
-
-use strict;
-use Getopt::Long;
-use Cwd qw/getcwd abs_path/;
-use File::Basename;
-use Sys::Hostname; # False laziness to get around a real config file...
-
-my %opts;
-
-GetOptions(\%opts, 'packager=s', 'release=s', 'server=s', 'dryrun');
-
-$ENV{PATH} = "/bin:/usr/bin";
-
-sub usage {
- print STDERR "usage: enrpm --packager <Your Name <you\@example.com>> [--release=<rpm-release-string>] --server <server-name> [--dryrun] tarball|module-name\n";
- print STDERR "where:\n\t--packager is the name and e-mail address of the packager for the .spec file\n\t--release is the rpm release number for the .spec file\n\t--server is the server to build the SRPM on\n\t--dryrun means do not actually run the commands\n\n";
- print STDERR "\ttarball is the name of a tarball containing the Perl module to be packaged\n";
- print STDERR "\tmodule-name is the name of a CPAN Perl module to be packaged\n";
- exit;
-}
-
-# Feeble excuse for not having a real configuration file
-my $user = 'ivan';
-my $buildsys = "/home/$user/buildsys";
-#my $rembuild = '/home/rsiddall/buildsys/ref';
-my $remdeps = '/home/ivan';
-my $rpmtopdir = "/home/$user/redhat";
-my $packager = 'Ivan Kohler <ivan@freeside.biz>';
-my $server = '10.5.4.5';
-
-$server = $opts{server} if defined($opts{server});
-$user = $1 if $server =~ /(\w+)\@[\w\d\.]+/;
-
-$packager = $opts{packager} if defined($opts{packager});
-
-# Go ahead and build an SRPM...
-
-my $ovidignore = '--ignore ' . join ('--ignore ', qw/libwww/);
-
-
-for my $file (@ARGV) {
- my ($name,$version,$type);
-
- if ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.(tar)\.(?:gz|bz2)$/) {
- $name = $1;
- $version = $2;
- $type = $3;
- } elsif ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.tgz$/) {
- $name = $1;
- $version = $2;
- $type = 'tar';
- } elsif ($file =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)\.(zip)$/) {
- $name = $1;
- $version = $2;
- $type = $3;
- } else {
- # keep things happy if we get "Foo-Bar" instead of "Foo::Bar"
- $file =~ s/-/::/g;
- $name = $file;
- $version = undef;
- $type = 'module';
- }
-
- if ($type eq 'module') {
- my $fluteopts = '';
- $fluteopts .= "--packager='$packager' " if defined($packager);
- $fluteopts .= "--release='$opts{release}' " if defined($opts{release});
- do_cmd("ssh $user\@$server \"ovid --deps $ovidignore $name | tail -1 | $remdeps/ovid2flute $fluteopts | /bin/sh\"");
- } else {
- my $absfile = abs_path($file);
- my $fname = basename($file);
- die "Packager name and e-mail required" if !defined($packager);
- die "RPM release string required" if !defined($opts{release});
- do_cmd("scp $absfile $user\@$server:/home/$user/$fname");
-# do_cmd("ssh $user\@$server 'cpanflute2 --just-spec --noperlreqs --email=\\\"$packager\\\" --release=$opts{release} /home/$user/$fname > /home/$user/work/redhat/SPECS/$name.spec;'");
-# do_cmd("ssh $user\@$server 'perl -pi -e \\\"s/perl\(perl\)/perl/g\\\" /home/$user/work/redhat/SPECS/$name.spec;'");
-# do_cmd("ssh $user\@$server 'rpmbuild -bs --nodeps --define \\\"_sourcedir /home/$user/\\\" --define \\\"_srcrpmdir /home/$user/work/redhat/SRPMS\\\" /home/$user/work/redhat/SPECS/$name.spec'");
- open SCRIPT, ">/home/$user/makesrpm" or die "Can't create SRPM construction script: ", $!;
- print SCRIPT "#!/bin/sh\n\n";
- print SCRIPT "cpanflute2 --just-spec --noperlreqs --email='$packager' --release=$opts{release} /home/$user/$fname > $rpmtopdir/SPECS/$name.spec;\n";
- print SCRIPT "perl -pi -e 's/perl(perl)/perl/g' $rpmtopdir/SPECS/$name.spec\n";
- print SCRIPT "rpmbuild -bs --nodeps --define '_sourcedir /home/$user/' --define '_srcrpmdir $rpmtopdir/SRPMS' $rpmtopdir/SPECS/$name.spec\n";
- close SCRIPT or die "Can't close SRPM construction script: ", $!;
- chmod(0711, "/home/$user/makesrpm");
- do_cmd("scp /home/$user/makesrpm $user\@$server:/home/$user/makesrpm");
- do_cmd("ssh $user\@$server /home/$user/makesrpm");
- }
- my $olddir = getcwd();
- do_cmd("$buildsys/build-freeside");
-# if (-x "$buildsys/refresh-repo") {
- do_cmd("$buildsys/refresh-repo");
-# } else {
-# do_cmd("ssh $user\@$server $rembuild/refresh-repo");
-# }
-}
-
-sub do_cmd {
- my $cmd = shift;
-
- print "$cmd\n";
- `$cmd` if !exists($opts{dryrun});
-}
-
-1;
-
+++ /dev/null
-#!/usr/bin/expect
-set p "not our actual passphrase"
-set f [lindex $argv 0]
-#spawn /bin/rpm --resign $f
-spawn /usr/bin/rpm --resign $f
-expect "Enter pass phrase:"
-send -- "$p\r"
-expect eof
+++ /dev/null
-#!/usr/bin/expect
-set password "not our actual passphrase"
-set key [lindex $argv 0]
-set output [lindex $argv 1]
-set input [lindex $argv 2]
-spawn gpg -sab --yes -u "$key" -o $output $input
-expect "Enter passphrase:"
-send -- "$password\r"
-expect eof
+++ /dev/null
-#!/usr/bin/python -tt
-import os
-config_opts['root'] = 'centos-5-i386'
-config_opts['target_arch'] = 'i386'
-
-config_opts['cleanup_on_failure'] = 0
-
-config_opts['chroot_setup_cmd'] = 'install buildsys-build'
-
-# caching related options
-#these are probably obsolete?
-config_opts['rebuild_cache'] = False
-#config_opts['use_cache'] = False
-config_opts['use_cache'] = True
-config_opts['pack_cmd'] = "/usr/sbin/mock-helper pack"
-config_opts['unpack_cmd'] = "/usr/sbin/mock-helper unpack"
-config_opts['cache_ext'] = ".tar.gz"
-config_opts['cache_topdir'] = "/var/cache/mock"
-#config_opts['max_cache_age_days'] = 15
-config_opts['max_cache_age_days'] = 150
-
-# config_opts['plugin_conf']['ccache_enable'] = True
-config_opts['plugin_conf']['ccache_enable'] = False
-# config_opts['plugin_conf']['ccache_opts']['max_cache_size'] = '4G'
-# config_opts['plugin_conf']['ccache_opts']['dir'] = "%(cache_topdir)s/%(root)s/ccache/"
-config_opts['plugin_conf']['yum_cache_enable'] = True
-config_opts['plugin_conf']['yum_cache_opts']['max_age_days'] = 30
-config_opts['plugin_conf']['yum_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/yum_cache/"
-config_opts['plugin_conf']['root_cache_enable'] = True
-config_opts['plugin_conf']['root_cache_opts']['max_age_days'] = 15
-config_opts['plugin_conf']['root_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/root_cache/"
-config_opts['plugin_conf']['root_cache_opts']['compress_program'] = "gzip"
-config_opts['plugin_conf']['root_cache_opts']['extension'] = ".gz"
-
-
-config_opts['yum.conf'] = """
-
-[main]
-cachedir=/var/cache/yum
-debuglevel=1
-reposdir=/dev/null
-logfile=/var/log/yum.log
-retries=20
-obsoletes=1
-gpgcheck=0
-assumeyes=1
-
-# repos
-
-[os]
-name=os
-mirrorlist=http://mirrorlist.centos.org/?release=5&arch=i386&repo=os
-baseurl=http://mirror.centos.org/centos/5/os/i386/
-#baseurl=file:///home/rsiddall/mock/repos/centos/5/os/i386/
-
-[updates]
-name=updates
-mirrorlist=http://mirrorlist.centos.org/?release=5&arch=i386&repo=updates
-baseurl=http://mirror.centos.org/centos/5/updates/i386/
-#baseurl=file:///home/rsiddall/mock/repos/centos/5/updates/i386/
-
-[groups]
-name=groups
-baseurl=http://dev.centos.org/centos/buildsys/5/
-
-"""
-
-#something here is causing a problem, don't know what, see if we need anything
-#config_opts['macros'] = """
-#%_topdir /builddir/build
-#%_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
-#
-## Change the next two lines to reflect yourself.
-#
-#%packager Freeside Internet Services, Inc. <rpm@freeside.biz>
-##%vendor
-##%distribution
-#
-## please change this to reflect the Distro Tree and Repo hosting packages!
-##%dist <distro>.<yourtag>
-#%centos_ver 5
-#
-##%_smp_mflags -j1
-#
-#"""
-
-
+++ /dev/null
-#!/usr/bin/python -tt
-import os
-
-config_opts['root'] = 'centos-5-x86_64'
-config_opts['target_arch'] = 'x86_64'
-
-config_opts['cleanup_on_failure'] = 0
-
-config_opts['chroot_setup_cmd'] = 'install buildsys-build'
-
-# caching related options
-#these are probably obsolete?
-config_opts['rebuild_cache'] = False
-#config_opts['use_cache'] = False
-config_opts['use_cache'] = True
-config_opts['pack_cmd'] = "/usr/sbin/mock-helper pack"
-config_opts['unpack_cmd'] = "/usr/sbin/mock-helper unpack"
-config_opts['cache_ext'] = ".tar.gz"
-config_opts['cache_topdir'] = "/var/cache/mock"
-#config_opts['max_cache_age_days'] = 15
-config_opts['max_cache_age_days'] = 150
-
-# config_opts['plugin_conf']['ccache_enable'] = True
-config_opts['plugin_conf']['ccache_enable'] = False
-# config_opts['plugin_conf']['ccache_opts']['max_cache_size'] = '4G'
-# config_opts['plugin_conf']['ccache_opts']['dir'] = "%(cache_topdir)s/%(root)s/ccache/"
-config_opts['plugin_conf']['yum_cache_enable'] = True
-config_opts['plugin_conf']['yum_cache_opts']['max_age_days'] = 30
-config_opts['plugin_conf']['yum_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/yum_cache/"
-config_opts['plugin_conf']['root_cache_enable'] = True
-config_opts['plugin_conf']['root_cache_opts']['max_age_days'] = 15
-config_opts['plugin_conf']['root_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/root_cache/"
-config_opts['plugin_conf']['root_cache_opts']['compress_program'] = "gzip"
-config_opts['plugin_conf']['root_cache_opts']['extension'] = ".gz"
-
-
-config_opts['yum.conf'] = """
-
-[main]
-cachedir=/var/cache/yum
-debuglevel=1
-reposdir=/dev/null
-logfile=/var/log/yum.log
-retries=20
-obsoletes=1
-gpgcheck=0
-assumeyes=1
-exclude=[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz]*.i*86 g[abcdefghijkmnopqrstuvwxyz]*.i?86 glib2.i?86 glib.i?86 *-devel.i?86
-# repos
-
-[os]
-name=os
-mirrorlist=http://mirrorlist.centos.org/?release=5&arch=x86_64&repo=os
-baseurl=http://mirror.centos.org/centos/5/os/x86_64/
-#baseurl=file:///home/rsiddall/mock/repos/centos/5/os/x86_64/
-
-[updates]
-name=updates
-mirrorlist=http://mirrorlist.centos.org/?release=5&arch=x86_64&repo=updates
-baseurl=http://mirror.centos.org/centos/5/updates/x86_64/
-#baseurl=file:///home/rsiddall/mock/repos/centos/5/updates/x86_64/
-
-[groups]
-name=groups
-baseurl=http://dev.centos.org/centos/buildsys/5/
-"""
-
-#something here is causing a problem, don't know what, see if we need anything
-#re-enabled
-#config_opts['macros'] = """
-#%_topdir /builddir/build
-#%_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
-#
-## Change the next two lines to reflect yourself.
-#
-#%packager Freeside Internet Services, Inc. <rpm@freeside.biz>
-##%vendor
-##%distribution
-#
-## please change this to reflect the Distro Tree and Repo hosting packages!
-##%dist <distro>.<yourtag>
-#%centos_ver 5
-#
-##%_smp_mflags -j1
-#
-#"""
-
-
+++ /dev/null
-# mock defaults
-#
-# Define default values here.
-# These values are overwritten in the /etc/mock/CHROOT.cfg files.
-#
-# Example:
-#
-# config_opts['foo'] = bar
-config_opts['basedir'] = '/var/lib/mock/'
-config_opts['chroot'] = '/usr/sbin/mock-helper chroot'
-config_opts['mount'] = '/usr/sbin/mock-helper mount'
-config_opts['umount'] = '/usr/sbin/mock-helper umount'
-config_opts['rm'] = '/usr/sbin/mock-helper rm'
-config_opts['mknod'] = '/usr/sbin/mock-helper mknod'
-config_opts['yum'] = '/usr/sbin/mock-helper yum'
-config_opts['runuser'] = '/sbin/runuser'
-config_opts['chrootuser'] = 'mockbuild'
-config_opts['chrootgroup'] = 'mockbuild'
-config_opts['chrootuid'] = os.geteuid()
-config_opts['chrootgid'] = os.getegid()
-config_opts['chroothome'] = '/builddir'
-config_opts['clean'] = True
-
-# caching related options
-config_opts['rebuild_cache'] = False
-config_opts['use_cache'] = False
-config_opts['pack_cmd'] = "/usr/sbin/mock-helper pack"
-config_opts['unpack_cmd'] = "/usr/sbin/mock-helper unpack"
-config_opts['cache_ext'] = ".tar.gz"
-config_opts['cache_topdir'] = "root-cache"
-config_opts['max_cache_age_days'] = 15
-
-# allow some network tests to run under the chroot
-config_opts['files']['/etc/resolv.conf'] = open("/etc/resolv.conf","r").read()
-config_opts['files']['/etc/hosts'] = open("/etc/hosts","r").read()
-
-config_opts['chroot_setup_cmd'] = 'install buildsys-build'
-#config_opts['chroot_setup_cmd'] = 'groupinstall build'
-
+++ /dev/null
-[formatters]
-keys: detailed,simple,unadorned,state
-
-[handlers]
-keys: simple_console,detailed_console,unadorned_console,simple_console_warnings_only
-
-[loggers]
-keys: root,build,state,mock
-
-[formatter_state]
-format: %(asctime)s - %(message)s
-
-[formatter_unadorned]
-format: %(message)s
-
-[formatter_simple]
-format: %(levelname)s: %(message)s
-
-;useful for debugging:
-[formatter_detailed]
-format: %(levelname)s %(filename)s:%(lineno)d: %(message)s
-
-[handler_unadorned_console]
-class: StreamHandler
-args: []
-formatter: unadorned
-level: INFO
-
-[handler_simple_console]
-class: StreamHandler
-args: []
-formatter: simple
-level: INFO
-
-[handler_simple_console_warnings_only]
-class: StreamHandler
-args: []
-formatter: simple
-level: WARNING
-
-[handler_detailed_console]
-class: StreamHandler
-args: []
-formatter: detailed
-level: WARNING
-
-; usually dont want to set a level for loggers
-; this way all handlers get all messages, and messages can be filtered
-; at the handler level
-;
-; all these loggers default to a console output handler
-;
-[logger_root]
-level: NOTSET
-handlers: simple_console
-
-; mock logger normally has no output
-; catches stuff like mock.trace_decorator and mock.util
-; dont normally want to propagate to root logger, either
-[logger_mock]
-level: NOTSET
-handlers:
-qualname: mock
-propagate: 1
-
-[logger_state]
-level: NOTSET
-; unadorned_console only outputs INFO or above
-handlers: unadorned_console
-qualname: mock.Root.state
-propagate: 0
-
-[logger_build]
-level: NOTSET
-handlers: simple_console_warnings_only
-qualname: mock.Root.build
-propagate: 0
-
-; the following is a list mock logger qualnames used within the code:
-;
-; qualname: mock.util
-; qualname: mock.uid
-; qualname: mock.trace_decorator
-
+++ /dev/null
-# mock defaults
-# vim:tw=0:ts=4:sw=4:et:
-#
-# This config file is for site-specific default values that apply across all
-# configurations. Options specified in this config file can be overridden in
-# the individual mock config files.
-#
-# The defaults.cfg delivered by default has NO options set. Only set options
-# here if you want to override the defaults.
-#
-# Entries in this file follow the same format as other mock config files.
-# config_opts['foo'] = bar
-
-#############################################################################
-#
-# Things that we recommend you set in defaults.cfg:
-#
-# config_opts['basedir'] = '/var/lib/mock/'
-# config_opts['cache_topdir'] = '/var/cache/mock'
-# Note: the path pointed to by basedir and cache_topdir must be owned
-# by group 'mock' and must have mode: g+rws
-# config_opts['rpmbuild_timeout'] = 0
-# config_opts['use_host_resolv'] = True
-
-# You can configure log format to pull from logging.ini formats of these names:
-# config_opts['build_log_fmt_name'] = "unadorned"
-# config_opts['root_log_fmt_name'] = "detailed"
-# config_opts['state_log_fmt_name'] = "state"
-#
-# mock will normally set up a minimal chroot /dev.
-# If you want to use a pre-configured /dev, disable this and use the bind-mount
-# plugin to mount your special /dev
-# config_opts['internal_dev_setup'] = True
-#
-# internal_setarch defaults to 'True' if the python 'ctypes' package is
-# available. It is in the python std lib on >= python 2.5. On older versions,
-# it is available as an addon. On systems w/o ctypes, it will default to
-# 'False'
-# config_opts['internal_setarch'] = False
-#
-# the cleanup_on_* options allow you to automatically clean and remove the
-# mock build directory, but only take effect if --resultdir is used.
-# config_opts provides fine-grained control. cmdline only has big hammer
-#
-# config_opts['cleanup_on_success'] = 1
-# config_opts['cleanup_on_failure'] = 1
-
-#############################################################################
-#
-# plugin related. Below are the defaults. Change to suit your site
-# policy. defaults.cfg is a good place to do this.
-#
-# NOTE: Some of the caching options can theoretically affect build
-# reproducability. Change with care.
-#
-# config_opts['plugin_conf']['ccache_enable'] = True
-# config_opts['plugin_conf']['ccache_opts']['max_cache_size'] = '4G'
-# config_opts['plugin_conf']['ccache_opts']['dir'] = "%(cache_topdir)s/%(root)s/ccache/"
-# config_opts['plugin_conf']['yum_cache_enable'] = True
-# config_opts['plugin_conf']['yum_cache_opts']['max_age_days'] = 30
-# config_opts['plugin_conf']['yum_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/yum_cache/"
-# config_opts['plugin_conf']['root_cache_enable'] = True
-# config_opts['plugin_conf']['root_cache_opts']['max_age_days'] = 15
-# config_opts['plugin_conf']['root_cache_opts']['dir'] = "%(cache_topdir)s/%(root)s/root_cache/"
-# config_opts['plugin_conf']['root_cache_opts']['compress_program'] = "gzip"
-# config_opts['plugin_conf']['root_cache_opts']['extension'] = ".gz"
-#
-# bind mount plugin is enabled by default but has no configured directories to mount
-# config_opts['plugin_conf']['bind_mount_enable'] = True
-# config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/host/path', '/bind/mount/path/in/chroot/' ))
-#
-# config_opts['plugin_conf']['tmpfs_enable'] = False
-# config_opts['plugin_conf']['tmpfs_opts'] = {'required_ram_mb': 1024}
-
-#############################################################################
-#
-# Things that you can change, but we dont recommend it:
-# config_opts['chroothome'] = '/builddir'
-# config_opts['clean'] = True
-
-#############################################################################
-#
-# Things that are best suited for individual chroot config files:
-#
-# MUST SET (in individual chroot cfg file):
-# config_opts['root'] = 'name-of-yum-build-dir'
-# config_opts['target_arch'] = 'i386'
-# config_opts['yum.conf'] = ''
-#
-# CAN SET, defaults usually work ok:
-# config_opts['chroot_setup_cmd'] = 'install buildsys-build'
-# config_opts['log_config_file'] = 'logging.ini'
-# config_opts['more_buildreqs']['srpm_name-version-release'] = 'dependencies'
-# config_opts['macros']['Add_your_macro_name_here'] = "add macro value here"
-# config_opts['files']['path/name/no/leading/slash'] = "put file contents here."
-# config_opts['chrootuid'] = os.getuid()
-# config_opts['chrootgid'] = grp.getgrnam("mock")[2]
-# config_opts['useradd'] = '/usr/sbin/useradd -m -u %(uid)s -g %(gid)s -d %(home)s -n %(user)s' # Fedora/RedHat
+++ /dev/null
-#!/usr/bin/python -tt
-
-import os
-
-config_opts['root'] = 'sles-10-i386'
-config_opts['basedir'] = '/var/lib/mock/'
-config_opts['chroot'] = '/usr/sbin/mock-helper chroot'
-config_opts['mount'] = '/usr/sbin/mock-helper mount'
-config_opts['umount'] = '/usr/sbin/mock-helper umount'
-config_opts['rm'] = '/usr/sbin/mock-helper rm'
-config_opts['mknod'] = '/usr/sbin/mock-helper mknod'
-config_opts['yum'] = '/usr/sbin/mock-helper yum'
-config_opts['runuser'] = '/bin/su'
-config_opts['chroot_setup_cmd'] = 'groupinstall build build-minimal build-base'
-config_opts['chrootuser'] = 'mockbuild'
-config_opts['chrootgroup'] = 'users'
-config_opts['chrootuid'] = os.geteuid()
-config_opts['chrootgid'] = os.getegid()
-config_opts['chroothome'] = '/builddir'
-config_opts['clean'] = True
-config_opts['target_arch'] = 'i386'
-config_opts['use_cache'] = 1
-
-
-config_opts['yum.conf'] = """
-[main]
-cachedir=/var/cache/yum
-debuglevel=1
-logfile=/var/log/yum.log
-reposdir=/dev/null
-retries=20
-obsoletes=1
-gpgcheck=0
-assumeyes=1
-
-# repos
-
-[base]
-name=base
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/sles10/base/i386/
-#baseurl=http://redshank.elirion.net/sles
-baseurl=file:///home/rsiddall/mock/repos/sles10/i386
-
-[build]
-name=build
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/sles10/base/i386/
-#baseurl=http://grosbeak.elirion.net/repo/sles/10/build/i386/
-baseurl=file:///home/rsiddall/public_html/repo/sles/10/build/i386
-
-[groups]
-name=groups
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/mock/buildgroups/sles10/i386/
-#baseurl=http://redshank.elirion.net/slesgroup/sles10/i386/
-baseurl=file:///home/rsiddall/mock/buildgroups/sles10/i386
-
-"""
-
-
-
+++ /dev/null
-#!/usr/bin/python -tt
-
-import os
-
-config_opts['root'] = 'sles-10-x86_64'
-config_opts['basedir'] = '/var/lib/mock/'
-config_opts['chroot'] = '/usr/sbin/mock-helper chroot'
-config_opts['mount'] = '/usr/sbin/mock-helper mount'
-config_opts['umount'] = '/usr/sbin/mock-helper umount'
-config_opts['rm'] = '/usr/sbin/mock-helper rm'
-config_opts['mknod'] = '/usr/sbin/mock-helper mknod'
-config_opts['yum'] = '/usr/sbin/mock-helper yum'
-config_opts['runuser'] = '/bin/su'
-config_opts['chroot_setup_cmd'] = 'groupinstall build build-minimal build-base'
-config_opts['chrootuser'] = 'mockbuild'
-config_opts['chrootgroup'] = 'users'
-config_opts['chrootuid'] = os.geteuid()
-config_opts['chrootgid'] = os.getegid()
-config_opts['chroothome'] = '/builddir'
-config_opts['clean'] = True
-config_opts['target_arch'] = 'x86_64'
-config_opts['use_cache'] = 1
-
-
-config_opts['yum.conf'] = """
-[main]
-cachedir=/var/cache/yum
-debuglevel=1
-logfile=/var/log/yum.log
-reposdir=/dev/null
-retries=20
-obsoletes=1
-gpgcheck=0
-assumeyes=1
-
-# repos
-
-[base]
-name=base
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/sles10/base/x86_64/
-#baseurl=http://redshank.elirion.net/sles
-baseurl=file:///home/rsiddall/mock/repos/sles10/x86_64
-
-[build]
-name=build
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/sles10/base/x86_64/
-#baseurl=http://grosbeak.elirion.net/repo/sles/10/build/x86_64/
-baseurl=file:///home/rsiddall/public_html/repo/sles/10/build/x86_64
-
-[groups]
-name=groups
-#baseurl=http://hb.linuxdev.us.dell.com/pub/yum/mock/buildgroups/sles10/x86_64/
-#baseurl=http://redshank.elirion.net/slesgroup/sles10/x86_64/
-baseurl=file:///home/rsiddall/mock/buildgroups/sles10/x86_64
-
-"""
-
-
-
+++ /dev/null
---- Package.pm.orig 2007-05-25 09:54:14.000000000 -0400
-+++ Package.pm 2007-07-07 15:35:20.000000000 -0400
-@@ -165,6 +165,7 @@
- push @out, $self->provreq($n);
- }
-
-+ return join("\n", map { "Provides: $_"; } @out) if scalar(@out) > 5;
- return join('', 'Provides: ', join ' ', @out);
- }
-
-@@ -376,10 +377,15 @@
-
- %install
-
--make PREFIX=%{_prefix} \
-- DESTDIR=%{buildroot} \
-- INSTALLDIRS=@installdirs@ \
-- install
-+if [ -f Build.PL -a -f Build ] ; then
-+ ./Build destdir=%{buildroot} \
-+ install
-+else
-+ make PREFIX=%{_prefix} \
-+ DESTDIR=%{buildroot} \
-+ INSTALLDIRS=@installdirs@ \
-+ install
-+fi
-
- [ -x /usr/lib/rpm/brp-compress ] && /usr/lib/rpm/brp-compress
-
+++ /dev/null
-#!/bin/sh
-#
-# Copyright 2008, Elirion, Inc. All rights reserved.
-# This software is licensed under the same terms as Freeside itself.
-#
-# This script builds SRPMs if the Freeside CVS contents have changed.
-# It must have reference copies of the Freeside versions it builds.
-# Each SRPM's "release" is set to the date & time the script is run.
-# The version number is forced to the CVS version. The version and release
-# hard-coded in the last .spec file committed to CVS are NOT used.
-#
-source $HOME/freeside-cvs
-RELEASE=`date +%Y%m%d%H%M%S`
-QUIET_FLAG=
-#FORCE_FLAG=0
-FORCE_FLAG=1
-#VERSIONS='1.7 1.9'
-VERSIONS='1.7'
-
-while getopts "fhqv:" flag
-do
- case $flag in
- f)
- echo "Force mode"
- FORCE_FLAG=1;;
- q)
- echo "Quiet mode"
- QUIET_FLAG=-q;;
- v)
- echo "Changing versions from $VERSIONS to $OPTARG"
- VERSIONS=$OPTARG;;
- *)
- usage;;
- esac
-done
-
-usage() {
- echo "build-from-cvs: build SRPMs if the Freeside CVS contents have changed"
- echo "where:"
- echo " -f: force building SRPMs even if CVS is unchanged"
- echo " -h: print this usage information"
- echo " -q: run quietly"
- echo " -v <versions>: change versions (currently: $VERSIONS)"
- exit 0
-}
-
-for VERSION in $VERSIONS; do
- echo ${VERSION}
- /bin/rm -rf ref-${VERSION}
- cp -pr freeside-${VERSION} ref-${VERSION}
- cd freeside-${VERSION}
- cvs update -d -P
- cd ..
- diff -qr --exclude=CVS freeside-${VERSION} ref-${VERSION}
- RETVAL=$?
- if [ $FORCE_FLAG = 1 -o $RETVAL -gt 0 ]; then
- # Build the tarball with the modified .spec file in it, hard-coding the release into the .spec file
- cd freeside-${VERSION}
- for SPECFILE in install/rpm/freeside.spec rpm/freeside.spec; do
- if [ -f $SPECFILE ]; then
- cp -pf $SPECFILE ..
- perl -p -i -e "s/\d+[^\}]+/${VERSION}/ if /%define\s+version\s+(\d+[^\}]+)\}/;" ${SPECFILE}
- perl -pi -e "s/\$1/${RELEASE}/ if /%define\s+release\s+(\d+)/;" $SPECFILE
- tar zcvf $HOME/redhat/SOURCES/freeside-${VERSION}.tar.gz --exclude CVS ../freeside-${VERSION}
- mv -f ../`basename $SPECFILE` `dirname $SPECFILE`
- fi
- done
- cd ..
- rpmbuild -ts $HOME/redhat/SOURCES/freeside-${VERSION}.tar.gz
- # Could do a koji-build here
- # Or move the SRPM to a staging directory for the build machine to check
- # Should make the Bundles and check the dependencies for changes
- fi
- /bin/rm -rf ref-${VERSION}
-done
+++ /dev/null
-export CVSROOT=":pserver:anonymous:anonymous@cvs.freeside.biz:/home/cvs/cvsroot"
-export CVS_RSH="ssh"
+++ /dev/null
-#!/bin/sh
-
-cpanflute2 --just-spec --noperlreqs --email='Ivan Kohler <ivan@freeside.biz>' --release=1 /home/rsiddall/Business-OnlinePayment-Jety-0.06.tar.gz > /home/rsiddall/redhat/SPECS/Business-OnlinePayment-Jety.spec;
-perl -pi -e 's/perl(perl)/perl/g' /home/rsiddall/redhat/SPECS/Business-OnlinePayment-Jety.spec
-rpmbuild -bs --nodeps --define '_sourcedir /home/rsiddall/' --define '_srcrpmdir /home/rsiddall/redhat/SRPMS' /home/rsiddall/redhat/SPECS/Business-OnlinePayment-Jety.spec
+++ /dev/null
-#!/usr/bin/perl -w
-#
-# Convert the output from ovid --deps into a list of modules to run through cpanflute2 to get
-# better .spec files, and generate SRPMs from these.
-
-use strict;
-use Getopt::Long;
-
-# Need to fix up modules where Ovid parses the name incorrectly
-my %badparse=(
- 'Crypt-PasswdMD-5' => 'Crypt-PasswdMD5',
- 'IPC-Run-3' => 'IPC-Run3',
- 'Digest-SHA-1' => 'Digest-SHA1',
- 'Digest-MD-4' => 'Digest-MD4',
-);
-
-my %extra_buildreqs=(
- 'File-Rsync' => [ qw/rsync/ ],
- 'MIMETools' => [ qw/perl(Mail::Header) perl(Mail::Internet) perl(Mail::Field) perl(IO::Stringy) perl(File::Temp)/ ],
- 'HTML::Scrubber' => [ qw/perl(HTML::Parser) perl(HTML::Tagset)/ ],
-# 'Time::Duration' => [ qw/perl(Test::Pod) perl(Test::Pod::Coverage)/ ],
-);
-
-my %extra_reqs=(
- 'File-Rsync' => [ qw/rsync/ ],
-);
-
-my %opts;
-
-GetOptions(\%opts, 'packager=s', 'release=s');
-
-$opts{packager} = 'Richard Siddall <richard.siddall@elirion.net>' if !defined($opts{packager});
-
-# Need to process modules that cause Ovid to crash
-#my @extramods= qw/Text::CSV_XS Pod::Simple Crypt::SSLeay/;
-#my @extramods= qw/Crypt::SSLeay/;
-my @extramods= qw/IPC::ShareLite/;
-
-my $specfiledir="/home/ivan/work/redhat/SPECS";
-$specfiledir = "/home/ivan/redhat/SPECS" if ! -d $specfiledir;
-my $srpmdir="/home/ivan/work/redhat/SRPMS";
-$srpmdir = "/home/ivan/redhat/SRPMS" if ! -d $srpmdir;
-
-foreach (reverse <STDIN>) {
- next if !defined($_);
- #print $_;
- last if $_ !~ /^([-\w:]+) perl-([-\w\.]+)(-\d+[\w\.]+?)$/;
- #print "$1 = $2\n"
- my ($name, $rpm, $ver) = ($1, $2, $3);
- print "# $name ($rpm - $ver)\n";
- foreach my $cand (keys %badparse) {
- if ($rpm =~ /^$cand/) {
- $rpm =~ s/^$cand/$badparse{$cand}/;
- print "# Fixed up $rpm\n";
- last;
- }
- }
- printcmds($name, "$rpm$ver");
- #print "cpanspec -v --packager 'Unknown <nobody\@example.com>' $name\n";
- #print "echo $rpm\n";
- #print "repoquery perl-$rpm\n";
- #touchsrpms($name, "$rpm$ver");
-}
-
-exit;
-
-foreach my $name (@extramods) {
- my $rpm = $name;
- $rpm =~ s/::/-/g;
- my $tarball = `find ~/.cpan/sources -name '$rpm-*' -print | tail -1`;
- #print "$name (Extra!)\n"
- if ($tarball =~ /\/($rpm-.*?)(\.tar\.gz|\.tgz)\s*$/) {
- $rpm = $1;
- printcmds($name, $rpm);
- } else {
- die "Can't find full rpm name for $name in \"$tarball\"\n";
- }
-}
-
-sub touchsrpms {
- my ($name, $rpm) = @_;
- my $repofolder ="/var/www/html/repo/sles/10/freeside-1.9/stable/x86_64";
- my $srpmfolder ="/home/ivan/work/redhat/SRPMS";
- my @files = glob "$repofolder/perl-$rpm-*.rpm";
- if (! scalar(@files)) {
- print "Can't find $rpm in $repofolder\n";
- @files = glob "$srpmfolder/perl-$rpm-*.src.rpm\n";
- if (scalar(@files)) {
- my $file = pop @files;
- print "touch $file\n";
- } else {
- print STDERR "No corresponding SRPM: $srpmfolder/perl-$rpm-*.src.rpm\n";
- printcmds($name, $rpm);
- }
- }
-}
-
-sub printcmds {
- my ($name, $rpm) = @_;
-
- my $tarball = `find ~/.cpan -name '$rpm.tar.gz' -print`;
- $tarball = `find ~/.cpan -name '$rpm.tgz' -print` if ! $tarball;
- chomp($tarball);
-# my $specfile = "$specfiledir/$name.spec";
- my $rpmname = $rpm;
- $rpmname = $1 if $rpm =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)$/;
- my $specfile = "$specfiledir/perl-$rpmname.spec";
- $specfile =~ s/::/-/g;
- # Work out which version of the SRPM we're going to generate.
- my $rel;
- if (!defined($opts{release})) {
- for ($rel = 1; ; $rel++) {
- my $srpm = "$srpmdir/perl-$rpm-$rel.src.rpm";
- last if ! -e $srpm;
- print "# File exists: $srpm\n";
- }
- } else {
- $rel = $opts{release};
- }
- my $fluteopts = "";
- foreach my $cand (keys %extra_buildreqs) {
- if ($rpm =~ /^$cand/) {
- $fluteopts .= join "", map { "--buildrequires $_ " } @{$extra_buildreqs{$cand}};
- last;
- }
- }
- foreach my $cand (keys %extra_reqs) {
- if ($rpm =~ /^$cand/) {
- $fluteopts .= join "", map { "--requires $_ " } @{$extra_reqs{$cand}};
- last;
- }
- }
- print "cpanflute2 --just-spec --noperlreqs --email='$opts{packager}' --release=$rel $fluteopts $tarball > $specfile;\n";
- # Should fix up the .spec file
- print "perl -pi -e 's/perl\\(perl\\)/perl/g' $specfile;\n";
- my $tarballdir = `dirname $tarball` or die "Can't find tarball for $name: $tarball\n";
- chomp($tarballdir);
- print "rpmbuild -bs --nodeps --define '_sourcedir $tarballdir' --define '_srcrpmdir $srpmdir' $specfile\n";
- print "# Generates: $srpmdir/perl-$rpm-$rel.src.rpm\n";
-}
-
+++ /dev/null
-#!/usr/bin/perl -w
-#
-# Convert the output from ovid --deps into a list of modules to run through cpanflute2 to get
-# better .spec files, and generate SRPMs from these.
-
-use strict;
-use Getopt::Long;
-
-# Need to fix up modules where Ovid parses the name incorrectly
-my %badparse=(
- 'Crypt-PasswdMD-5' => 'Crypt-PasswdMD5',
- 'IPC-Run-3' => 'IPC-Run3',
- 'Digest-SHA-1' => 'Digest-SHA1',
- 'Digest-MD-4' => 'Digest-MD4',
-);
-
-my %extra_buildreqs=(
- 'File-Rsync' => [ qw/rsync/ ],
- 'MIMETools' => [ qw/perl(Mail::Header) perl(Mail::Internet) perl(Mail::Field) perl(IO::Stringy) perl(File::Temp)/ ],
- 'HTML::Scrubber' => [ qw/perl(HTML::Parser) perl(HTML::Tagset)/ ],
-# 'Time::Duration' => [ qw/perl(Test::Pod) perl(Test::Pod::Coverage)/ ],
-);
-
-my %extra_reqs=(
- 'File-Rsync' => [ qw/rsync/ ],
-);
-
-my %opts;
-
-GetOptions(\%opts, 'packager=s', 'release=s');
-
-$opts{packager} = 'Richard Siddall <richard.siddall@elirion.net>' if !defined($opts{packager});
-
-# Need to process modules that cause Ovid to crash
-#my @extramods= qw/Text::CSV_XS Pod::Simple Crypt::SSLeay/;
-#my @extramods= qw/Crypt::SSLeay/;
-my @extramods= qw/IPC::ShareLite/;
-
-my $specfiledir="/home/rsiddall/work/redhat/SPECS";
-my $srpmdir="/home/rsiddall/work/redhat/SRPMS";
-
-foreach (reverse <STDIN>) {
- next if !defined($_);
- #print $_;
- last if $_ !~ /^([-\w:]+) perl-([-\w\.]+)(-\d+[\w\.]+?)$/;
- #print "$1 = $2\n"
- my ($name, $rpm, $ver) = ($1, $2, $3);
- print "# $name ($rpm - $ver)\n";
- foreach my $cand (keys %badparse) {
- if ($rpm =~ /^$cand/) {
- $rpm =~ s/^$cand/$badparse{$cand}/;
- print "# Fixed up $rpm\n";
- last;
- }
- }
- printcmds($name, "$rpm$ver");
- #print "cpanspec -v --packager 'Unknown <nobody\@example.com>' $name\n";
- #print "echo $rpm\n";
- #print "repoquery perl-$rpm\n";
- #touchsrpms($name, "$rpm$ver");
-}
-
-exit;
-
-foreach my $name (@extramods) {
- my $rpm = $name;
- $rpm =~ s/::/-/g;
- my $tarball = `find ~/.cpan/sources -name '$rpm-*' -print | tail -1`;
- #print "$name (Extra!)\n"
- if ($tarball =~ /\/($rpm-.*?)(\.tar\.gz|\.tgz)\s*$/) {
- $rpm = $1;
- printcmds($name, $rpm);
- } else {
- die "Can't find full rpm name for $name in \"$tarball\"\n";
- }
-}
-
-sub touchsrpms {
- my ($name, $rpm) = @_;
- my $repofolder ="/var/www/html/repo/sles/10/freeside-1.9/stable/x86_64";
- my $srpmfolder ="/home/rsiddall/work/redhat/SRPMS";
- my @files = glob "$repofolder/perl-$rpm-*.rpm";
- if (! scalar(@files)) {
- print "Can't find $rpm in $repofolder\n";
- @files = glob "$srpmfolder/perl-$rpm-*.src.rpm\n";
- if (scalar(@files)) {
- my $file = pop @files;
- print "touch $file\n";
- } else {
- print STDERR "No corresponding SRPM: $srpmfolder/perl-$rpm-*.src.rpm\n";
- printcmds($name, $rpm);
- }
- }
-}
-
-sub printcmds {
- my ($name, $rpm) = @_;
-
- my $tarball = `find ~/.cpan -name '$rpm.tar.gz' -print`;
- $tarball = `find ~/.cpan -name '$rpm.tgz' -print` if ! $tarball;
- chomp($tarball);
-# my $specfile = "$specfiledir/$name.spec";
- my $rpmname = $rpm;
- $rpmname = $1 if $rpm =~ /^(?:.*\/)?(.*)-(?:v\.?)?([^-]+)$/;
- my $specfile = "$specfiledir/perl-$rpmname.spec";
- $specfile =~ s/::/-/g;
- # Work out which version of the SRPM we're going to generate.
- my $rel;
- if (!defined($opts{release})) {
- for ($rel = 1; ; $rel++) {
- my $srpm = "$srpmdir/perl-$rpm-$rel.src.rpm";
- last if ! -e $srpm;
- print "# File exists: $srpm\n";
- }
- } else {
- $rel = $opts{release};
- }
- my $fluteopts = "";
- foreach my $cand (keys %extra_buildreqs) {
- if ($rpm =~ /^$cand/) {
- $fluteopts .= join "", map { "--buildrequires $_ " } @{$extra_buildreqs{$cand}};
- last;
- }
- }
- foreach my $cand (keys %extra_reqs) {
- if ($rpm =~ /^$cand/) {
- $fluteopts .= join "", map { "--requires $_ " } @{$extra_reqs{$cand}};
- last;
- }
- }
- print "cpanflute2 --just-spec --noperlreqs --email='$opts{packager}' --release=$rel $fluteopts $tarball > $specfile;\n";
- # Should fix up the .spec file
- print "perl -pi -e 's/perl\\(perl\\)/perl/g' $specfile;\n";
- my $tarballdir = `dirname $tarball` or die "Can't find tarball for $name: $tarball\n";
- chomp($tarballdir);
- print "rpmbuild -bs --nodeps --define '_sourcedir $tarballdir' --define '_srcrpmdir $srpmdir' $specfile\n";
- print "# Generates: $srpmdir/perl-$rpm-$rel.src.rpm\n";
-}
-
+++ /dev/null
-#!/bin/sh
-#
-# Copyright 2008, Elirion, Inc. All rights reserved.
-# This software is licensed under the same terms as Freeside itself.
-#
-# This script iterates through all the specified Freeside repositories, running
-# both yum-arch and createrepo to update the yum repository meta-data.
-# The script should be run after the repository contents are changed.
-#
-# TBD: Run yum-arch, createrepo, or both, as appropriate for the distro and version
-# the repository is targetted for.
-#
-DISTROS='centos sles'
-CENTOSVERS='4 5'
-SLESVERS=10
-WHICHVERS=
-VERSIONS='1.7 1.9'
-ARCHS='i386 x86_64'
-REPOS='testing stable prerelease'
-RPMS=
-KEYID=rsiddall
-SAVEDIR=$HOME
-
-REPOBASEFOLDER=/var/www/html
-
-QUIET_FLAG=
-
-BUILDSYSDIR=`dirname $0`
-
-if [ -f $BUILDSYSDIR/buildsysrc ]; then
- #chmod a+x $BUILDSYSDIR/buildsysrc
- #echo $BUILDSYSDIR/buildsysrc
- . $BUILDSYSDIR/buildsysrc
-fi
-if [ -f $HOME/buildsysrc ]; then
- #chmod a+x $HOME/buildsysrc
- #echo $HOME/buildsysrc
- . $HOME/buildsysrc
-fi
-
-
-usage() {
- echo "refresh-repo: refresh yum metadata for all yum repositories"
- echo "where:"
- echo " -a <archs>: change architectures (currently: $ARCHS)"
- echo " -d <distros>: change distributions (currently: $DISTROS)"
- echo " -r <repos>: change repositories (currently: $REPOS)"
- echo " -v <versions>: change versions (currently: $VERSIONS)"
- echo " -w <distvers>: change distro version (currently: $WHICHVERS)"
- exit 0
-}
-
-while getopts "a:d:hqr:v:w:" flag
-do
- case $flag in
- a)
- echo "Changing architectures from $ARCHS to $OPTARG"
- ARCHS=$OPTARG;;
- d)
- echo "Changing distros from $DISTROS to $OPTARG"
- DISTROS=$OPTARG;;
- q)
- echo "Quiet mode"
- QUIET_FLAG=-q;;
- r)
- echo "Changing repository from $REPOS to $OPTARG"
- REPOS=$OPTARG;;
- v)
- echo "Changing versions from $VERSIONS to $OPTARG"
- VERSIONS=$OPTARG;;
- w)
- echo "Changing which distro versions from $WHICHVERS to $OPTARG"
- WHICHVERS=$OPTARG;;
- *)
- usage;;
- esac
-done
-
-#for DISTRO in ${DISTROS}; do
-# for VERSION in ${VERSIONS}; do
-# for REPO in ${REPOS}; do
-# for ARCH in ${ARCHS}; do
-# # Determine which RPMs need to be signed
-# NEWRPMS=`rpm --checksig $REPOBASEFOLDER/repo/$DISTROS/$DISTVERS/freeside-${VERSION}/${REPO}/${ARCH}/*.rpm | grep -v ' gpg ' | cut -d ':' -f 1 | tr '\n' ' '`
-# RPMS=`echo "$RPMS $NEWRPMS"`
-# done
-# done
-# done
-#done
-##rpm --addsign $RPMS
-#for RPM in $RPMS; do
-# ./expect-addsign $RPM
-#done
-for DISTRO in ${DISTROS}; do
- for VERSION in ${VERSIONS}; do
- for REPO in ${REPOS}; do
- for ARCH in ${ARCHS}; do
- if [ "${WHICHVERS}x" = "x" ]; then
- if [ "$DISTRO" = "centos" ]; then
- DISTVERS=$CENTOSVERS
- fi
- if [ "$DISTRO" = "sles" ]; then
- DISTVERS=$SLESVERS
- fi
- else
- DISTVERS=$WHICHVERS
- fi
- for distver in $DISTVERS
- do
- # Update the repo information
- echo "${DISTRO}-${distver}: $VERSION - $REPO - $ARCH"
- DIR=$REPOBASEFOLDER/repo/$DISTRO/$distver/freeside-${VERSION}/${REPO}/${ARCH}
- if [ -d $DIR ]
- then
- # SLES requires signed repodata. Save any existing files so we don't regenerate
- for ext in asc key
- do
- if [ -e $DIR/repodata/repomd.xml.${ext} ]
- then
- mv $DIR/repodata/repomd.xml.${ext} $SAVEDIR
- fi
- done
- if [ "$DISTRO" = "sles" ]
- then
- for file in $DIR/freeside-mysql-*.rpm
- do
- mv $file $file.old
- done
- for file in $DIR/freeside-selfservice-*.rpm
- do
- mv $file $DIR/../self-service/$ARCH
- done
- fi
- if [ "$DISTRO-$distver" = "centos-4" ]
- then
- yum-arch $QUIET_FLAG $DIR/
- fi
-# createrepo $QUIET_FLAG --checkts $DIR/
- createrepo $QUIET_FLAG $DIR/
- if [ "$DISTRO" = "sles" ]
- then
- # SLES requires signed repodata...
- if [ -e $SAVEDIR/repomd.xml.asc ]
- then
- mv $SAVEDIR/repomd.xml.asc $DIR/repodata
- fi
-
-# gpg -sab --yes -u "$KEYID" -o $DIR/repodata/repomd.xml.asc $DIR/repodata/repomd.xml
- ./expect-signrepo $KEYID $DIR/repodata/repomd.xml.asc $DIR/repodata/repomd.xml
- if [ -e $SAVEDIR/repomd.xml.key ]
- then
- mv $SAVEDIR/repomd.xml.key $DIR/repodata
- else
- gpg -a --yes -u "$KEYID" --export -o $DIR/repodata/repomd.xml.key
- fi
- fi
- else
- echo "No such folder $DIR - skipping"
- fi
- done
- done
- done
- done
-done
etc/RT_Config.pm
etc/RT_SiteConfig.pm
lib/RT/Config.pm
-lib/RT/CustomField_Overlay.pm #customfield date patch
-lib/RT/Interface/Web.pm #customfield date patch
lib/RT/Interface/Web_Vendor.pm
- lib/RT/Record.pm #and customfield date patch
+ lib/RT/Record.pm
lib/RT/SearchBuilder.pm #need DBIx::SearchBuilder >= 1.36 for Pg 8.1+
lib/RT/Transaction_Overlay.pm
-lib/RT/Tickets_Overlay.pm #customfield date patch
lib/RT/Ticket_Overlay.pm
lib/RT/Users_Overlay.pm
lib/RT/Groups_Overlay.pm
share/html/Admin/Users/Modify.html
share/html/Elements/ColumnMap
share/html/Elements/CollectionList
-share/html/Elements/EditCustomFieldDate #customfield date patch (NEW)
share/html/Elements/Header
share/html/Elements/PageLayout
#html/Elements/QuickCreate
- share/html/Elements/RefreshHomepage
- share/html/Elements/RT__CustomField/ColumnMap
- share/html/Elements/RT__Ticket/ColumnMap
- share/html/Elements/ShowCustomFieldDate #customfield date patch (NEW)
share/html/Elements/SelectDate
-share/html/Elements/ShowLink_Checklist
- share/html/Elements/ShowUserVerbose
share/html/Elements/Footer
html/Ticket/Create.html #XXX TODO
share/html/Search/Build.html
- share/html/Search/Elements/BuildFormatString
- share/html/Search/Elements/PickCFs #customfield date patch
-share/html/Ticket/Checklist.html
share/html/Ticket/Display.html
share/html/Ticket/Elements/AddCustomers
share/html/Ticket/Elements/EditCustomers
share/html/Ticket/Elements/ShowCustomers
-share/html/Ticket/Elements/ShowMembers_Checklist
- share/html/Ticket/Elements/BulkLinks
share/html/Ticket/Elements/ShowSummary
share/html/Ticket/Elements/ShowTransactionAttachments
share/html/Ticket/Elements/Tabs
- share/html/Ticket/Graph/index.html
share/html/Ticket/ModifyCustomers.html
html/NoAuth/css/3.5-default/main.css
html/NoAuth/css/3.5-default/misc.css
share/html/Prefs/SearchOptions.html
share/html/Widgets/TitleBoxEnd
-
-share/html/Callbacks/RTx-Checklist/*
+++ /dev/null
-#!/usr/bin/perl -w
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use warnings;
-use strict;
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("lib", "local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
-
-use RT;
-RT::LoadConfig();
-RT->InitLogging();
-if (RT->Config->Get('DevelMode')) { require Module::Refresh; }
-
-RT::CheckPerlRequirements();
-RT->InitPluginPaths();
-
-my $explicit_port = shift @ARGV;
-my $port = $explicit_port || RT->Config->Get('WebPort') || '8080';
-
-
-require RT::Handle;
-my ($integrity, $state, $msg) = RT::Handle->CheckIntegrity;
-
-unless ( $integrity ) {
- print STDERR <<EOF;
-
-RT couldn't connect to the database where tickets are stored.
-If this is a new installation of RT, you should visit the URL below
-to configure RT and initialize your database.
-
-If this is an existing RT installation, this may indicate a database
-connectivity problem.
-
-The error RT got back when trying to connect to your database was:
-
-$msg
-
-EOF
-
- require RT::Installer;
- # don't enter install mode if the file exists but is unwritable
- if (-e RT::Installer->ConfigFile && !-w _) {
- die 'Since your configuration exists ('
- . RT::Installer->ConfigFile
- . ") but is not writable, I'm refusing to do anything.\n";
- }
-
- RT->Config->Set( 'LexiconLanguages' => '*' );
- RT::I18N->Init;
-
- RT->InstallMode(1);
-} else {
- RT->ConnectToDatabase();
- RT->InitSystemObjects();
- RT->InitClasses( Heavy => 1 );
- RT->InitPlugins();
- RT->Config->PostLoadCheck();
-
- my ($status, $msg) = RT::Handle->CheckCompatibility(
- $RT::Handle->dbh, 'post'
- );
- unless ( $status ) {
- print STDERR $msg, "\n\n";
- exit -1;
- }
-}
-
-require RT::Interface::Web::Standalone;
-my $server = RT::Interface::Web::Standalone->new;
-run_server($port);
-exit 0;
-
-sub run_server {
- my $port = shift;
- $server->port($port);
- eval { $server->run() };
-
- if ( my $err = $@ ) {
- handle_startup_error($err);
- }
-}
-
-sub handle_startup_error {
- my $err = shift;
- if ( $err =~ /bind: Permission denied/ ) {
- handle_bind_error();
- } else {
- die
- "Something went wrong while trying to run RT's standalone web server:\n\t"
- . $err;
- }
-}
-
-
-sub handle_bind_error {
-
- print STDERR <<EOF;
-WARNING: RT couldn't start up a web server on port @{[$port]}.
-This is often the case if you're running @{[$0]} as
-someone other than your system's "root" user.
-EOF
-
- if ($explicit_port) {
- print STDERR
- "Please check your system configuration or choose another port\n\n";
- } else {
- print STDERR "\nFor now, RT has chosen an alternate port to run on.\n\n";
- if ( !$integrity ) {
- print STDERR <<EOF;
-You can use this server to configure and explore RT. While configuring
-RT, you'll have a chance to set a permanent port and URL for your
-server.
-
-EOF
- }
- run_server( 8000 + int( rand(1024) ) );
- }
-}
=cut
-Set($UsernameFormat, 'verbose');
+Set($UsernameFormat, 'concise');
=item C<$WebDomain>
Set ($DefaultSearchResultFormat, qq{
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
- Customer,
Status,
QueueName,
OwnerName,
'__NEWLINE__',
'',
'<small>__Requestors__</small>',
- '',
'<small>__CreatedRelative__</small>',
'<small>__ToldRelative__</small>',
'<small>__LastUpdatedRelative__</small>',
=cut
-Set(@Plugins, qw( RTx::Calendar )); #RTx::Checklist ));
+Set(@Plugins, (qw(RTx::Calendar)));
=back
=cut
-Set($UsernameFormat, 'verbose');
+Set($UsernameFormat, 'concise');
=item C<$WebDomain>
Set ($DefaultSearchResultFormat, qq{
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
'<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
- Customer,
Status,
QueueName,
OwnerName,
'__NEWLINE__',
'',
'<small>__Requestors__</small>',
- '',
'<small>__CreatedRelative__</small>',
'<small>__ToldRelative__</small>',
'<small>__LastUpdatedRelative__</small>',
=cut
-Set(@Plugins, (qw(RTx::Calendar))); #RTx::Checklist ));
+Set(@Plugins, (qw(RTx::Calendar)));
=back
# These settings are user-editable.
Set($WebDefaultStylesheet, 'freeside2.1');
-Set($UsernameFormat, 'verbose'); #back to concise to hide email addresses
#uncomment to use
#Set($DefaultSummaryRows, 10);
my $value = $self->Get('RTAddressRegexp');
return if $value;
- #XXX freeside - should fix this at some point, but it is being WAY
- #too noisy in the logs
- #$RT::Logger->error(
- # 'The RTAddressRegexp option is not set in the config.'
- # .' Not setting this option results in additional SQL queries to'
- # .' check whether each address belongs to RT or not.'
- # .' It is especially important to set this option if RT recieves'
- # .' emails on addresses that are not in the database or config.'
- #);
+ $RT::Logger->error(
+ 'The RTAddressRegexp option is not set in the config.'
+ .' Not setting this option results in additional SQL queries to'
+ .' check whether each address belongs to RT or not.'
+ .' It is especially important to set this option if RT recieves'
+ .' emails on addresses that are not in the database or config.'
+ );
},
},
# User overridable mail options
'Enter one value with autocompletion', # loc
'Enter up to [_1] values with autocompletion', # loc
],
- Date => [
- 'Select multiple dates', # loc
- 'Select date', # loc
- 'Select up to [_1] dates', # loc
- ],
);
sub TypeComposites {
my $self = shift;
- return grep !/(?:[Tt]ext|Combobox|Date)-0/, map { ("$_-1", "$_-0") } $self->Types;
+ return grep !/(?:[Tt]ext|Combobox)-0/, map { ("$_-1", "$_-0") } $self->Types;
}
=head2 SetLookupType
$extra_values--;
}
}
- # For date, we need to store Content as ISO date
- if ($self->Type eq 'Date') {
- my $DateObj = new RT::Date( $self->CurrentUser );
- $DateObj->Set(
- Format => 'unknown',
- Value => $args{'Content'},
- );
- $args{'Content'} = $DateObj->ISO;
- }
my $newval = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
my $val = $newval->Create(
ObjectType => ref($obj),
$values_hash{$val} = 1 if $val;
}
- # For Date Cfs, @values is empty when there is no changes (no datas in form input)
- return @results if ( $cf->Type eq 'Date' && ! @values );
-
$cf_values->RedoSearch;
while ( my $cf_value = $cf_values->Next ) {
next if $values_hash{ $cf_value->id };
}
my $new_content = $new_value->Content;
-
- # For date, we need to display them in "human" format in result message
- if ($cf->Type eq 'Date') {
- my $DateObj = new RT::Date( $self->CurrentUser );
- $DateObj->Set(
- Format => 'ISO',
- Value => $new_content,
- );
- $new_content = $DateObj->AsString;
-
- if ( defined $old_content && length $old_content ) {
- $DateObj->Set(
- Format => 'ISO',
- Value => $old_content,
- );
- $old_content = $DateObj->AsString;
- }
- }
-
unless ( defined $old_content && length $old_content ) {
return ( $new_value_id, $self->loc( "[_1] [_2] added", $cf->Name, $new_content ));
}
return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $Msg ) );
}
- my $old_value = $TransactionObj->OldValue;
- # For date, we need to display them in "human" format in result message
- if ( $cf->Type eq 'Date' ) {
- my $DateObj = new RT::Date( $self->CurrentUser );
- $DateObj->Set(
- Format => 'ISO',
- Value => $old_value,
- );
- $old_value = $DateObj->AsString;
- }
return (
$TransactionId,
$self->loc(
"[_1] is no longer a value for custom field [_2]",
- $old_value, $cf->Name
+ $TransactionObj->OldValue, $cf->Name
)
);
}
# }}}
+ # {{{ Deal with auto-customer association
+
+ #unless we already have (a) customer(s)...
+ unless ( $self->Customers->Count ) {
+
+ #first find any requestors with emails but *without* customer targets
+ my @NoCust_Requestors =
+ grep { $_->EmailAddress && ! $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ for my $Requestor (@NoCust_Requestors) {
+
+ #perhaps the stuff in here should be in a User method??
+ my @Customers =
+ &RT::URI::freeside::email_search( email=>$Requestor->EmailAddress );
+
+ foreach my $custnum ( map $_->{'custnum'}, @Customers ) {
+
+ ## false laziness w/RT/Interface/Web_Vendor.pm
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Requestor->_AddLink(@link);
+ #XXX should do something with $msg# push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ #find any requestors with customer targets
+
+ my %cust_target = ();
+
+ my @Requestors =
+ grep { $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ foreach my $Requestor ( @Requestors ) {
+ foreach my $cust_link ( @{ $Requestor->Customers->ItemsArrayRef } ) {
+ $cust_target{ $cust_link->Target } = 1;
+ }
+ }
+
+ #and then auto-associate this ticket with those customers
+
+ foreach my $cust_target ( keys %cust_target ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ #'Target' => "freeside://freeside/cust_main/$custnum",
+ 'Target' => $cust_target,
+ );
+
+ my( $val, $msg ) = $self->_AddLink(@link);
+ push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ # }}}
+
# {{{ Add all the custom fields
foreach my $arg ( keys %args ) {
next;
}
}
-
- #don't show transactions for reminders
- my $silent = ( !$args{'_RecordTransaction'}
- || $self->Type eq 'reminder'
- );
-
+
my ( $wval, $wmsg ) = $self->_AddLink(
Type => $LINKTYPEMAP{$type}->{'Type'},
$LINKTYPEMAP{$type}->{'Mode'} => $link,
- Silent => $silent,
+ Silent => !$args{'_RecordTransaction'},
'Silent'. ( $LINKTYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' )
=> 1,
);
}
# }}}
-
- # {{{ Deal with auto-customer association
-
- #unless we already have (a) customer(s)...
- unless ( $self->Customers->Count ) {
-
- #first find any requestors with emails but *without* customer targets
- my @NoCust_Requestors =
- grep { $_->EmailAddress && ! $_->Customers->Count }
- @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
-
- for my $Requestor (@NoCust_Requestors) {
-
- #perhaps the stuff in here should be in a User method??
- my @Customers =
- &RT::URI::freeside::email_search( email=>$Requestor->EmailAddress );
-
- foreach my $custnum ( map $_->{'custnum'}, @Customers ) {
-
- ## false laziness w/RT/Interface/Web_Vendor.pm
- my @link = ( 'Type' => 'MemberOf',
- 'Target' => "freeside://freeside/cust_main/$custnum",
- );
-
- my( $val, $msg ) = $Requestor->_AddLink(@link);
- #XXX should do something with $msg# push @non_fatal_errors, $msg;
-
- }
-
- }
-
- #find any requestors with customer targets
-
- my %cust_target = ();
-
- my @Requestors =
- grep { $_->Customers->Count }
- @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
-
- foreach my $Requestor ( @Requestors ) {
- foreach my $cust_link ( @{ $Requestor->Customers->ItemsArrayRef } ) {
- $cust_target{ $cust_link->Target } = 1;
- }
- }
-
- #and then auto-associate this ticket with those customers
-
- foreach my $cust_target ( keys %cust_target ) {
-
- my @link = ( 'Type' => 'MemberOf',
- #'Target' => "freeside://freeside/cust_main/$custnum",
- 'Target' => $cust_target,
- );
-
- my( $val, $msg ) = $self->_AddLink(@link);
- push @non_fatal_errors, $msg;
-
- }
-
- }
-
- # }}}
-
# Now that we've created the ticket and set up its metadata, we can actually go and check OwnTicket on the ticket itself.
# This might be different than before in cases where extensions like RTIR are doing clever things with RT's ACL system
if ( $DeferOwner ) {
);
}
- #don't make a transaction or fire off any scrips for reminders either
- if ( $args{'_RecordTransaction'} && $self->Type ne 'reminder' ) {
+ if ( $args{'_RecordTransaction'} ) {
# {{{ Add a transaction for the create
my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
QueueAdminCc => [ 'WATCHERFIELD' => 'AdminCc' => 'Queue', ], #loc_left_pair
QueueWatcher => [ 'WATCHERFIELD' => undef => 'Queue', ], #loc_left_pair
CustomFieldValue => [ 'CUSTOMFIELD', ], #loc_left_pair
- DateCustomFieldValue => [ 'DATECUSTOMFIELD', ],
CustomField => [ 'CUSTOMFIELD', ], #loc_left_pair
CF => [ 'CUSTOMFIELD', ], #loc_left_pair
Updated => [ 'TRANSDATE', ], #loc_left_pair
WATCHERFIELD => \&_WatcherLimit,
MEMBERSHIPFIELD => \&_WatcherMembershipLimit,
CUSTOMFIELD => \&_CustomFieldLimit,
- DATECUSTOMFIELD => \&_DateCustomFieldLimit,
HASATTRIBUTE => \&_HasAttributeLimit,
);
our %can_bundle = ();# WATCHERFIELD => "yes", );
return ($TicketCFs, $CFs);
}
-=head2 _DateCustomFieldLimit
-
-Limit based on CustomFields of type Date
-
-Meta Data:
- none
-
-=cut
-
-sub _DateCustomFieldLimit {
- my ( $self, $_field, $op, $value, %rest ) = @_;
-
- my $field = $rest{'SUBKEY'} || die "No field specified";
-
- # For our sanity, we can only limit on one queue at a time
-
- my ($queue, $cfid, $column);
- ($queue, $field, $cfid, $column) = $self->_CustomFieldDecipher( $field );
-
-# If we're trying to find custom fields that don't match something, we
-# want tickets where the custom field has no value at all. Note that
-# we explicitly don't include the "IS NULL" case, since we would
-# otherwise end up with a redundant clause.
-
- my $null_columns_ok;
- if ( ( $op =~ /^NOT LIKE$/i ) or ( $op eq '!=' ) ) {
- $null_columns_ok = 1;
- }
-
- my $cfkey = $cfid ? $cfid : "$queue.$field";
- my ($TicketCFs, $CFs) = $self->_CustomFieldJoin( $cfkey, $cfid, $field );
-
- $self->_OpenParen;
-
- if ( $CFs && !$cfid ) {
- $self->SUPER::Limit(
- ALIAS => $CFs,
- FIELD => 'Name',
- VALUE => $field,
- ENTRYAGGREGATOR => 'AND',
- );
- }
-
- $self->_OpenParen if $null_columns_ok;
-
- my $date = RT::Date->new( $self->CurrentUser );
- $date->Set( Format => 'unknown', Value => $value );
-
- if ( $op eq "=" ) {
-
- # if we're specifying =, that means we want everything on a
- # particular single day. in the database, we need to check for >
- # and < the edges of that day.
-
- $date->SetToMidnight( Timezone => 'server' );
- my $daystart = $date->ISO;
- $date->AddDay;
- my $dayend = $date->ISO;
-
- $self->_OpenParen;
-
- $self->_SQLLimit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => ">=",
- VALUE => $daystart,
- %rest,
- );
-
- $self->_SQLLimit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => "<=",
- VALUE => $dayend,
- %rest,
- ENTRYAGGREGATOR => 'AND',
- );
-
- $self->_CloseParen;
-
- }
- else {
- $self->_SQLLimit(
- ALIAS => $TicketCFs,
- FIELD => 'Content',
- OPERATOR => $op,
- VALUE => $date->ISO,
- %rest,
- );
- }
-
- $self->_CloseParen;
-
-}
-
=head2 _CustomFieldLimit
Limit based on CustomFields
}
push @res, { %$row, FIELD => "Priority", ORDER => $order } ;
-
- } elsif ( $field eq 'Customer' ) { #Freeside
-
- my $linkalias = $self->Join(
- TYPE => 'LEFT',
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'Links',
- FIELD2 => 'LocalBase'
- );
-
- $self->SUPER::Limit(
- LEFTJOIN => $linkalias,
- FIELD => 'Type',
- OPERATOR => '=',
- VALUE => 'MemberOf',
- );
- $self->SUPER::Limit(
- LEFTJOIN => $linkalias,
- FIELD => 'Target',
- OPERATOR => 'STARTSWITH',
- VALUE => 'freeside://freeside/cust_main/',
- );
-
- #if there was a Links.RemoteTarget int, this bs wouldn't be necessary
- my $custnum_sql = "CAST(SUBSTR($linkalias.Target,31) AS INTEGER)";
-
- if ( $subkey eq 'Number' ) {
-
- push @res, { %$row,
- ALIAS => '',
- FIELD => $custnum_sql,
- };
-
- } elsif ( $subkey eq 'Name' ) {
-
- my $custalias = $self->Join(
- TYPE => 'LEFT',
- EXPRESSION => $custnum_sql,
- TABLE2 => 'cust_main',
- FIELD2 => 'custnum',
-
- );
-
- my $field = "COALESCE( $custalias.company,
- $custalias.last || ', ' || $custalias.first
- )";
-
- push @res, { %$row, ALIAS => '', FIELD => $field };
-
- }
-
- } #Freeside
-
+ }
else {
push @res, $row;
}
$args{CUSTOMFIELD} = $CF->Id;
}
- # Handle special customfields types
- if ($CF->Type eq 'Date') {
- $args{FIELD} = 'DateCustomFieldValue';
- }
-
#If we are looking to compare with a null value.
if ( $args{'OPERATOR'} =~ /^is$/i ) {
$args{'DESCRIPTION'}
$pkey = $2;
unless ( $pkey ) {
- #way too noisy, using this prefix is normal usage# cluck "bad URL $uri";
+ cluck "bad URL $uri";
return(undef);
}
+++ /dev/null
-#!/usr/bin/perl -w
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-
-# As we specify that XML is UTF-8 and we output it to STDOUT, we must be sure
-# it is UTF-8 so further XMLin will not break
-binmode(STDOUT, ":utf8");
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("lib", "local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
-
-use RT;
-use XML::Simple;
-
-RT::LoadConfig();
-RT::Init();
-
-my $LocalOnly = @ARGV ? shift(@ARGV) : 1;
-
-my %RV;
-my %Ignore = (
- All => [qw(
- id Created Creator LastUpdated LastUpdatedBy
- )],
- Templates => [qw(
- TranslationOf
- )],
-);
-
-my $SystemUserId = $RT::SystemUser->Id;
-my @classes = qw(
- Users Groups Queues ScripActions ScripConditions
- Templates Scrips ACL CustomFields
-);
-foreach my $class (@classes) {
- require "RT/$class.pm";
- my $objects = "RT::$class"->new($RT::SystemUser);
- $objects->{find_disabled_rows} = 1;
- $objects->UnLimit;
-
- if ($class eq 'CustomFields') {
- $objects->OrderByCols(
- { FIELD => 'LookupType' },
- { FIELD => 'SortOrder' },
- { FIELD => 'Id' },
- );
- }
- else {
- $objects->OrderBy( FIELD => 'Id' );
- }
-
- if ($LocalOnly) {
- next if $class eq 'ACL'; # XXX - would go into infinite loop - XXX
- $objects->Limit( FIELD => 'LastUpdatedBy', OPERATOR => '!=', VALUE => $SystemUserId )
- unless $class eq 'Groups';
- $objects->Limit( FIELD => 'Id', OPERATOR => '!=', VALUE => $SystemUserId )
- if $class eq 'Users';
- $objects->Limit( FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined' )
- if $class eq 'Groups';
- }
-
- my %fields;
- while (my $obj = $objects->Next) {
- next if $obj->can('LastUpdatedBy') and $obj->LastUpdatedBy == $SystemUserId;
-
- if (!%fields) {
- %fields = map { $_ => 1 } keys %{$obj->_ClassAccessible};
- delete @fields{
- @{$Ignore{$class}||=[]},
- @{$Ignore{All}||=[]},
- };
- }
-
- my $rv;
- # next if $obj-> # skip default names
- foreach my $field (sort keys %fields) {
- my $value = $obj->__Value($field);
- $rv->{$field} = $value if ( defined ($value) && length($value) );
- }
- delete $rv->{Disabled} unless $rv->{Disabled};
-
- foreach my $record (map { /ACL/ ? 'ACE' : substr($_, 0, -1) } @classes) {
- foreach my $key (map "$record$_", ('', 'Id')) {
- next unless exists $rv->{$key};
- my $id = $rv->{$key} or next;
- my $obj = "RT::$record"->new($RT::SystemUser);
- $obj->LoadByCols( Id => $id ) or next;
- $rv->{$key} = $obj->__Value('Name') || 0;
- }
- }
-
- if ($class eq 'Users' and defined $obj->Privileged) {
- $rv->{Privileged} = int($obj->Privileged);
- }
- elsif ($class eq 'CustomFields') {
- my $values = $obj->Values;
- while (my $value = $values->Next) {
- push @{$rv->{Values}}, {
- map { ($_ => $value->__Value($_)) } qw(
- Name Description SortOrder
- ),
- };
- }
- }
-
- if (eval { require RT::Attributes; 1 }) {
- my $attributes = $obj->Attributes;
- while (my $attribute = $attributes->Next) {
- my $content = $attribute->Content;
- $rv->{Attributes}{$attribute->Name} = $content if length($content);
- }
- }
-
- push @{$RV{$class}}, $rv;
- }
-}
-
-print(<< ".");
-no strict; use XML::Simple; *_ = XMLin(do { local \$/; readline(DATA) }, ForceArray => [qw(
- @classes Values
-)], NoAttr => 1, SuppressEmpty => ''); *\$_ = (\$_{\$_} || []) for keys \%_; 1; # vim: ft=xml
-__DATA__
-.
-
-print XMLout(
- { map { ($_ => ($RV{$_} || [])) } @classes },
- RootName => 'InitialData',
- NoAttr => 1,
- SuppressEmpty => '',
- XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>',
-);
+++ /dev/null
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC
-%# <jesse@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/copyleft/gpl.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-% my $name = $NamePrefix.$CustomField->Id.'-Values';
-<& /Elements/SelectDate, Name => "$name", ShowTime => 0, current => 0 &> (<%$DateObj->AsString%>)
-
-<%INIT>
-my $DateObj = new RT::Date ( $session{'CurrentUser'} );
-$DateObj->Set( Format => 'ISO', Value => $Default );
-</%INIT>
-<%ARGS>
-$Object => undef
-$CustomField => undef
-$NamePrefix => undef
-$Default => undef
-$Values => undef
-$MaxValues => 1
-</%ARGS>
: ($args->{'PassArguments'});
my %pass = map { $_ => $args->{$_} } grep exists $args->{$_}, @pass;
- my $path = $m->request_path;
- $path =~ s(^/rt)(); #hacky, dunno why this happens
- my $uri = RT->Config->Get('WebPath') . $path;
+ my $uri = RT->Config->Get('WebPath') . $m->request_path;
my @res = (
\'<a href="',
return \$bookmark;
},
},
-
- Customer => {
- title => 'Customer', #loc
- attribute => 'Customer', #title/attribute/name... what does it all mean?
- value => sub {
- my $Ticket = shift;
- my @Customers = @{ $Ticket->Customers->ItemsArrayRef };
- my @CustResolvers = map $_->TargetURI->Resolver, @Customers;
- my @return = ();
- for ( 0 .. $#CustResolvers ) {
- my $c = @CustResolvers[$_];
- push @return, \'<A HREF="', $c->HREF, \'">', $c->AsString, \'</A>';
- push @return, \'<BR>' if scalar(@CustResolvers) > 1
- && $_ != $#CustResolvers;
- }
- @return;
- },
- },
-
};
# if no GPG support, then KeyOwnerName and KeyRequestors fall back to the regular
%#
%# END BPS TAGGED BLOCK }}}
<&|/Widgets/TitleBox, title => loc('Refresh')&>
-<form method="get" action="<% RT->Config->Get('WebPath') . $path %>">
+<form method="get" action="<% RT->Config->Get('WebPath') . $m->request_path %>">
<& /Elements/Refresh, Name => 'HomeRefreshInterval',
Default => $session{'home_refresh_interval'}||RT->Config->Get('HomePageRefreshInterval', $session{'CurrentUser'}) &>
<& /Elements/Submit, Label => loc('Go!') &>
</&>
</form>
-<%init>
-my $path = $m->request_path;
-$path =~ s(^/rt)(); #hacky, dunno why this happens
-</%init>
<IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $Name %>_date_button" STYLE="cursor: pointer" TITLE="Select date">
<script type="text/javascript">
Calendar.setup({
- inputField: <% $Name |n,js_string %>,
+ inputField: "<%$Name%>",
% if ( defined($ShowTime) && $ShowTime ) {
ifFormat: "%Y-%m-%d %H:%M",
showsTime: true,
% } else {
ifFormat: "%Y-%m-%d",
% }
- button: <% $Name.'_date_button' |n,js_string %>,
+ button: "<%$Name%>_date_button",
});
</script>
<%init>
+++ /dev/null
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC
-%# <jesse@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<%INIT>
- my $content = $Object->Content;
- my $DateObj = new RT::Date ( $session{'CurrentUser'} );
- $DateObj->Set( Format => 'ISO', Value => $content );
- $content = $DateObj->AsString;
-</%INIT>
-<%$content|n%>
-<%ARGS>
-$Object
-</%ARGS>
+++ /dev/null
-<a href="<%$URI->Resolver->HREF%>">
-% if ($URI->IsLocal) {
-% my $member = $URI->Object;
-% if (UNIVERSAL::isa($member, "RT::Ticket")) {
-% my $inactive = 0; #$member->QueueObj->IsInactiveStatus($member->Status);
-
-<span class="<% $inactive ? 'ticket-inactive' : '' %>">
-<IMG SRC="<%$fsurl%>images/<% $status2image{$member->Status} %>.png" BORDER=0>
-<%$member->Id%>: (<%$member->OwnerObj->Name%>) <%$member->Subject%>
-%# [<% loc($member->Status) %>]
-</span>
-
-% } elsif ( UNIVERSAL::can($member, 'Name')) {
-<%$URI->Resolver->AsString%>: <%$member->Name%>
-% } else {
-<%$URI->Resolver->AsString%>
-% }
-% } else {
-<%$URI->Resolver->AsString%>
-% }
-</a>
-<%ARGS>
-$URI => undef
-</%ARGS>
-<%once>
-
-my %status2image = (
- 'new' => 'square_add', #'bullet_add',
- 'open' => 'square', #'bullet_black',
- 'stalled' => 'error',
- 'resolved' => 'tick',
- 'rejected' => 'cross',
- #'deleted' => 'delete',
-);
-
-</%once>
%#
%# END BPS TAGGED BLOCK }}}
%# Released under the terms of version 2 of the GNU Public License
-% if ( $Address->phrase || $Address->comment ) {
-<% sprintf q{%s <%s> %s}, map $Address->$_, qw( phrase address comment ) %>
-% } else {
-<% $Address->address %>
-% }
+<%$Address->format%>\
<%INIT>
my ($phrase, $address, $comment);
# All the things we can display in the format string by default
my @fields = qw(
id QueueName Subject
-
- Customer
-
Status ExtendedStatus UpdateStatus
Type
Bookmark
NEWLINE
-
); # loc_qw
$m->callback( CallbackOnce => 1, CallbackName => 'SetFieldsOnce', Fields => \@fields );
# Add PAW sort
$fields{'Custom.Ownership'} = 1;
-$fields{"Customer.$_"} = 1 foreach qw( Number Name ); #Freeside
-
my @Order = split /\|/, $Order;
my @OrderBy = split /\|/, $OrderBy;
if ($Order =~ /\|/) {
my %line;
$line{'Name'} = "'CF.{" . $CustomField->Name . "}'";
$line{'Field'} = $CustomField->Name;
-
- # Op
- if ($CustomField->Type eq 'Date') {
- $line{'Op'} = {
- Type => 'component',
- Path => '/Elements/SelectDateRelation',
- Arguments => {},
- };
- } else {
- $line{'Op'} = {
- Type => 'component',
- Path => '/Elements/SelectCustomFieldOperator',
- Arguments => { True => loc("is"),
- False => loc("isn't"),
- TrueVal=> '=',
- FalseVal => '!=',
- },
- };
- }
-
- # Value
- if ($CustomField->Type eq 'Date') {
- $line{'Value'} = {
- Type => 'component',
- Path => '/Elements/SelectDate',
- Arguments => {},
- };
- } else {
- $line{'Value'} = {
- Type => 'component',
- Path => '/Elements/SelectCustomFieldValue',
- Arguments => { CustomField => $CustomField },
- };
- }
-
+ $line{'Op'} = {
+ Type => 'component',
+ Path => '/Elements/SelectCustomFieldOperator',
+ Arguments => { True => loc("is"),
+ False => loc("isn't"),
+ TrueVal=> '=',
+ FalseVal => '!=',
+ },
+ };
+ $line{'Value'} = {
+ Type => 'component',
+ Path => '/Elements/SelectCustomFieldValue',
+ Arguments => { CustomField => $CustomField },
+ };
push @lines, \%line;
}
+++ /dev/null
-<& /Elements/Header, Title => loc("Checklist for Ticket #[_1] [_2]", $Ticket->Id, $Ticket->Subject) &>
-<& /Ticket/Elements/Tabs,
- Ticket => $Ticket, current_tab => 'Ticket/Checklist.html?id='.$Ticket->id,
- Title => loc("Ticket Checklist # [_1] [_2]", $Ticket->Id, $Ticket->Subject) &>
-
-<& /Ticket/Elements/ShowMembers_Checklist, Ticket => $Ticket &>
-
-% if ( $show_hint ) {
-
-<A HREF="ModifyLinks.html?id=<%$Ticket->id%>">Link</A>
-or <A HREF="Create.html?Queue=<%$Ticket->QueueObj->Id%>&new-MemberOf=<%$Ticket->id%>">create</A>
-create child tickets to make a checklist.
-
-% }
-
-<%ARGS>
-$id => undef
-</%ARGS>
-
-<%INIT>
-
-my $Ticket = LoadTicket ($id);
-
-unless ($Ticket->CurrentUserHasRight('ShowTicket')) {
- Abort("No permission to view ticket");
-}
-
-my $show_hint = ! $Ticket->Members->Count;
-
-</%INIT>
<%INIT>
my %hash;
if ( $Tickets && $Tickets->Count ) {
- my $first_ticket = $Tickets->Next or last; #avoid errors on bulk delete
+ my $first_ticket = $Tickets->Next;
# we only show current links that eixst on all the tickets
for my $type ( qw/DependsOn DependedOnBy Members MemberOf RefersTo
ReferredToBy/ ) {
+++ /dev/null
-
-<style type="text/css">
-ul.checklist {
- list-style-type: none
-}
-</style>
-
-<ul class="checklist">
-% while (my $link = $members->Next) {
-<li><& /Elements/ShowLink_Checklist, URI => $link->BaseURI &><br />
-% if ($depth < 8) { #why only 8?
-<& /Ticket/Elements/ShowMembers_Checklist, Ticket => $link->BaseObj, depth => ($depth+1) &>
-% }
-</li>
-% }
-</ul>
-
-<%INIT>
-
-return unless $Ticket;
-my $members = $Ticket->Members;
-return unless $members->Count;
-
-</%INIT>
-
-<%ARGS>
-$Ticket => undef
-$depth => 1
-</%ARGS>
title => loc('Links'),
path => "Ticket/ModifyLinks.html?id=" . $id,
},
- _Ea => {
- title => loc('Checklist'),
- path => "Ticket/Checklist.html?id=" . $id,
- },
_Eb=> {
title => loc('Customers'),
path => "Ticket/ModifyCustomers.html?id=" . $id,
<& Elements/ShowGraph, %ARGS, Ticket => $ticket &>
-% my $path = $m->request_comp->path;
-% $path =~ s(^/rt)(); #hacky, dunno why this happens
-<form action="<% RT->Config->Get('WebPath') . $path %>">
+<form action="<% RT->Config->Get('WebPath') . $m->request_comp->path %>">
<input type="hidden" class="hidden" name="id" value="<% $id %>" />
<& Elements/EditGraphProperties, %ARGS, Ticket => $ticket &>