invoice template and config localization, #12367
authormark <mark>
Fri, 16 Sep 2011 00:15:48 +0000 (00:15 +0000)
committermark <mark>
Fri, 16 Sep 2011 00:15:48 +0000 (00:15 +0000)
23 files changed:
FS/FS/Conf.pm
FS/FS/L10N/en_ca.pm [new file with mode: 0644]
FS/FS/L10N/en_us.pm
FS/FS/L10N/fr_ca.pm [new file with mode: 0644]
FS/FS/L10N/fr_fr.pm [new file with mode: 0644]
FS/FS/Locales.pm
FS/FS/Schema.pm
FS/FS/conf.pm
FS/FS/cust_bill.pm
FS/FS/cust_main.pm
FS/FS/cust_main_Mixin.pm
FS/FS/part_export/shellcommands.pm
conf/invoice_html
conf/invoice_latex
conf/invoice_template
httemplate/config/config-download.cgi
httemplate/config/config-image.cgi
httemplate/config/config-process.cgi
httemplate/config/config-view.cgi
httemplate/config/config.cgi
httemplate/edit/cust_main/billing.html
httemplate/view/cust_bill-logo.cgi
httemplate/view/cust_main/billing.html

index 79b7d8c..7dcbf04 100644 (file)
@@ -47,16 +47,25 @@ but this may change in the future.
 
 =over 4
 
 
 =over 4
 
-=item new
+=item new [ HASHREF ]
 
 Create a new configuration object.
 
 
 Create a new configuration object.
 
+HASHREF may contain options to set the configuration context.  Currently 
+accepts C<locale>, and C<localeonly> to disable fallback to the null locale.
+
 =cut
 
 sub new {
 =cut
 
 sub new {
-  my($proto) = @_;
+  my($proto) = shift;
+  my $opts = shift || {};
   my($class) = ref($proto) || $proto;
   my($class) = ref($proto) || $proto;
-  my($self) = { 'base_dir' => $base_dir };
+  my $self = {
+    'base_dir'    => $base_dir,
+    'locale'      => $opts->{locale},
+    'localeonly'  => $opts->{localeonly}, # for config-view.cgi ONLY
+  };
+  warn "FS::Conf created with no locale fallback.\n" if $self->{localeonly};
   bless ($self, $class);
 }
 
   bless ($self, $class);
 }
 
@@ -108,14 +117,26 @@ sub _usecompat {
 sub _config {
   my($self,$name,$agentnum,$agentonly)=@_;
   my $hashref = { 'name' => $name };
 sub _config {
   my($self,$name,$agentnum,$agentonly)=@_;
   my $hashref = { 'name' => $name };
-  $hashref->{agentnum} = $agentnum;
   local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
   local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
-  my $cv = FS::Record::qsearchs('conf', $hashref);
-  if (!$agentonly && !$cv && defined($agentnum) && $agentnum) {
-    $hashref->{agentnum} = '';
-    $cv = FS::Record::qsearchs('conf', $hashref);
+  my $cv;
+  my @a = (
+    ($agentnum || ()),
+    ($agentonly && $agentnum ? () : '')
+  );
+  my @l = (
+    ($self->{locale} || ()),
+    ($self->{localeonly} && $self->{locale} ? () : '')
+  );
+  # try with the agentnum first, then fall back to no agentnum if allowed
+  foreach my $a (@a) {
+    $hashref->{agentnum} = $a;
+    foreach my $l (@l) {
+      $hashref->{locale} = $l;
+      $cv = FS::Record::qsearchs('conf', $hashref);
+      return $cv if $cv;
+    }
   }
   }
-  return $cv;
+  return undef;
 }
 
 sub config {
 }
 
 sub config {
@@ -268,10 +289,14 @@ sub set {
 
   warn "[FS::Conf] SET $name\n" if $DEBUG;
 
 
   warn "[FS::Conf] SET $name\n" if $DEBUG;
 
-  my $old = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum});
-  my $new = new FS::conf { $old ? $old->hash 
-                                : ('name' => $name, 'agentnum' => $agentnum)
-                         };
+  my $hashref = {
+    name => $name,
+    agentnum => $agentnum,
+    locale => $self->{locale}
+  };
+
+  my $old = FS::Record::qsearchs('conf', $hashref);
+  my $new = new FS::conf { $old ? $old->hash : %$hashref };
   $new->value($value);
 
   my $error;
   $new->value($value);
 
   my $error;
@@ -312,7 +337,7 @@ sub delete {
   return $self->_usecompat('delete', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
   return $self->_usecompat('delete', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
-  if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum}) ) {
+  if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum, locale => $self->{locale}}) ) {
     warn "[FS::Conf] DELETE $name\n" if $DEBUG;
 
     my $oldAutoCommit = $FS::UID::AutoCommit;
     warn "[FS::Conf] DELETE $name\n" if $DEBUG;
 
     my $oldAutoCommit = $FS::UID::AutoCommit;
@@ -1035,6 +1060,7 @@ my %payment_gateway_options = (
     'description' => 'Subject: header on email invoices.  Defaults to "Invoice".  The following substitutions are available: $name, $name_short, $invoice_number, and $invoice_date.',
     'type'        => 'text',
     'per_agent'   => 1,
     'description' => 'Subject: header on email invoices.  Defaults to "Invoice".  The following substitutions are available: $name, $name_short, $invoice_number, and $invoice_date.',
     'type'        => 'text',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1065,6 +1091,7 @@ my %payment_gateway_options = (
     'description' => 'Notes section for HTML invoices.  Defaults to the same data in invoice_latexnotes if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Notes section for HTML invoices.  Defaults to the same data in invoice_latexnotes if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1073,6 +1100,7 @@ my %payment_gateway_options = (
     'description' => 'Footer for HTML invoices.  Defaults to the same data in invoice_latexfooter if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Footer for HTML invoices.  Defaults to the same data in invoice_latexfooter if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1081,6 +1109,7 @@ my %payment_gateway_options = (
     'description' => 'Summary initial page for HTML invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Summary initial page for HTML invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1088,6 +1117,7 @@ my %payment_gateway_options = (
     'section'     => 'invoicing',
     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
     'type'        => 'textarea',
     'section'     => 'invoicing',
     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
     'type'        => 'textarea',
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1152,6 +1182,7 @@ and customer address. Include units.',
     'description' => 'Notes section for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Notes section for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1160,6 +1191,7 @@ and customer address. Include units.',
     'description' => 'Footer for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Footer for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1168,6 +1200,7 @@ and customer address. Include units.',
     'description' => 'Summary initial page for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Summary initial page for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1176,6 +1209,7 @@ and customer address. Include units.',
     'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1254,6 +1288,7 @@ and customer address. Include units.',
     'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
     'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -1832,7 +1867,12 @@ and customer address. Include units.',
     'section'     => 'UI',
     'description' => 'Default locale',
     'type'        => 'select',
     'section'     => 'UI',
     'description' => 'Default locale',
     'type'        => 'select',
-    'select_enum' => [ FS::Locales->locales ],
+    'options_sub' => sub {
+      map { $_ => FS::Locales->description($_) } FS::Locales->locales;
+    },
+    'option_sub'  => sub {
+      FS::Locales->description(shift)
+    },
   },
 
   {
   },
 
   {
@@ -3361,6 +3401,7 @@ and customer address. Include units.',
     'type'        => 'image',
     'per_agent'   => 1, #XXX just view/logo.cgi, which is for the global
                         #old-style editor anyway...?
     'type'        => 'image',
     'per_agent'   => 1, #XXX just view/logo.cgi, which is for the global
                         #old-style editor anyway...?
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -3369,6 +3410,7 @@ and customer address. Include units.',
     'description' => 'Company logo for printed and PDF invoices, in EPS format.',
     'type'        => 'image',
     'per_agent'   => 1, #XXX as above, kinda
     'description' => 'Company logo for printed and PDF invoices, in EPS format.',
     'type'        => 'image',
     'per_agent'   => 1, #XXX as above, kinda
+    'per_locale'  => 1,
   },
 
   {
   },
 
   {
@@ -4554,6 +4596,20 @@ and customer address. Include units.',
   },
   
   {
   },
   
   {
+    'key'         => 'available-locales',
+    'section'     => '',
+    'description' => 'Limit available locales (employee preferences, per-customer locale selection, etc.) to a particular set.',
+    'type'        => 'select-sub',
+    'multiple'    => 1,
+    'options_sub' => sub { 
+      map { $_ => FS::Locales->description($_) }
+      grep { $_ ne 'en_US' } 
+      FS::Locales->locales;
+    },
+    'option_sub'  => sub { FS::Locales->description(shift) },
+  },
+  
+  {
     'key'         => 'translate-auto-insert',
     'section'     => '',
     'description' => 'Auto-insert untranslated strings for selected non-en_US locales with their default/en_US values. DO NOT TURN THIS ON.',
     'key'         => 'translate-auto-insert',
     'section'     => '',
     'description' => 'Auto-insert untranslated strings for selected non-en_US locales with their default/en_US values. DO NOT TURN THIS ON.',
diff --git a/FS/FS/L10N/en_ca.pm b/FS/FS/L10N/en_ca.pm
new file mode 100644 (file)
index 0000000..1b71f7b
--- /dev/null
@@ -0,0 +1,4 @@
+package FS::L10N::en_ca;
+use base qw(FS::L10N::en_us);
+
+1;
index e8a592d..6ad136b 100644 (file)
@@ -1,6 +1,5 @@
 package FS::L10N::en_us;
 use base qw(FS::L10N);
 package FS::L10N::en_us;
 use base qw(FS::L10N);
-#use strict;
 
 our %Lexicon = ( _AUTO=>1 );
 
 
 our %Lexicon = ( _AUTO=>1 );
 
diff --git a/FS/FS/L10N/fr_ca.pm b/FS/FS/L10N/fr_ca.pm
new file mode 100644 (file)
index 0000000..22ede1b
--- /dev/null
@@ -0,0 +1,4 @@
+package FS::L10N::fr_ca;
+use base qw(FS::L10N::fr_fr);
+
+1;
diff --git a/FS/FS/L10N/fr_fr.pm b/FS/FS/L10N/fr_fr.pm
new file mode 100644 (file)
index 0000000..5372410
--- /dev/null
@@ -0,0 +1,6 @@
+package FS::L10N::fr_fr;
+use base qw(FS::L10N::DBI);
+
+our %Lexicon = ( _AUTO => 1 );
+
+1;
index 607f2be..351f478 100644 (file)
@@ -28,8 +28,11 @@ Returns a list of the available locales.
 =cut
 
 tie our %locales, 'Tie::IxHash',
 =cut
 
 tie our %locales, 'Tie::IxHash',
-  'en_US', { name => 'English', country => 'United States', },
-  'iw_IL', { name => 'Hebrew',  country => 'Israel', rtl=>1, },
+  'en_CA', { name => 'English',     country => 'Canada', },
+  'en_US', { name => 'English',     country => 'United States', },
+  'fr_CA', { name => 'French',      country => 'Canada', },
+  'fr_FR', { name => 'French',      country => 'France', },
+  'iw_IL', { name => 'Hebrew',      country => 'Israel', rtl=>1, },
 ;
 
 sub locales {
 ;
 
 sub locales {
@@ -47,6 +50,17 @@ sub locale_info {
   %{ $locales{$locale} };
 }
 
   %{ $locales{$locale} };
 }
 
+=item description LOCALE
+
+Returns "Language (Country)" for a locale.
+
+=cut
+
+sub description {
+  my($class, $locale) = @_;
+  $locales{$locale}->{'name'} . ' (' . $locales{$locale}->{'country'} . ')';
+}
+
 =back
 
 =head1 BUGS
 =back
 
 =head1 BUGS
index 67ce21b..b36205c 100644 (file)
@@ -884,6 +884,7 @@ sub tables_hashref {
         'accountcode_cdr', 'char', 'NULL', 1, '', '',
         'billday',   'int', 'NULL', '', '', '',
         'edit_subject', 'char', 'NULL', 1, '', '',
         'accountcode_cdr', 'char', 'NULL', 1, '', '',
         'billday',   'int', 'NULL', '', '', '',
         'edit_subject', 'char', 'NULL', 1, '', '',
+        'locale', 'varchar', 'NULL', 16, '', '', 
       ],
       'primary_key' => 'custnum',
       'unique' => [ [ 'agentnum', 'agent_custid' ] ],
       ],
       'primary_key' => 'custnum',
       'unique' => [ [ 'agentnum', 'agent_custid' ] ],
@@ -3265,13 +3266,14 @@ sub tables_hashref {
 
     'conf' => {
       'columns' => [
 
     'conf' => {
       'columns' => [
-        'confnum',  'serial',  '', '', '', '', 
-        'agentnum', 'int',  'NULL', '', '', '', 
-        'name',     'varchar', '', $char_d, '', '', 
-        'value',    'text', 'NULL', '', '', '',
+        'confnum',  'serial',     '',      '', '', '', 
+        'agentnum', 'int',    'NULL',      '', '', '', 
+        'locale',   'varchar','NULL',      16, '', '',
+        'name',     'varchar',    '', $char_d, '', '', 
+        'value',    'text',   'NULL',      '', '', '',
       ],
       'primary_key' => 'confnum',
       ],
       'primary_key' => 'confnum',
-      'unique' => [ [ 'agentnum', 'name' ]],
+      'unique' => [ [ 'agentnum', 'locale', 'name' ] ],
       'index' => [],
     },
 
       'index' => [],
     },
 
index 3faab14..b467cec 100644 (file)
@@ -3,6 +3,7 @@ package FS::conf;
 use strict;
 use vars qw( @ISA );
 use FS::Record;
 use strict;
 use vars qw( @ISA );
 use FS::Record;
+use FS::Locales;
 
 @ISA = qw(FS::Record);
 
 
 @ISA = qw(FS::Record);
 
@@ -94,6 +95,7 @@ sub check {
     || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
     || $self->ut_text('name')
     || $self->ut_anything('value')
     || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
     || $self->ut_text('name')
     || $self->ut_anything('value')
+    || $self->ut_enum('locale', [ '', FS::Locales->locales ])
   ;
   return $error if $error;
 
   ;
   return $error if $error;
 
index d8d310a..6a604e0 100644 (file)
@@ -1,8 +1,9 @@
 package FS::cust_bill;
 
 use strict;
 package FS::cust_bill;
 
 use strict;
-use vars qw( @ISA $DEBUG $me $conf
+use vars qw( @ISA $DEBUG $me 
              $money_char $date_format $rdate_format $date_format_long );
              $money_char $date_format $rdate_format $date_format_long );
+             # but NOT $conf
 use vars qw( $invoice_lines @buf ); #yuck
 use Fcntl qw(:flock); #for spool_csv
 use Cwd;
 use vars qw( $invoice_lines @buf ); #yuck
 use Fcntl qw(:flock); #for spool_csv
 use Cwd;
@@ -41,6 +42,7 @@ use FS::bill_batch;
 use FS::cust_bill_batch;
 use FS::cust_bill_pay_pkg;
 use FS::cust_credit_bill_pkg;
 use FS::cust_bill_batch;
 use FS::cust_bill_pay_pkg;
 use FS::cust_credit_bill_pkg;
+use FS::L10N;
 
 @ISA = qw( FS::cust_main_Mixin FS::Record );
 
 
 @ISA = qw( FS::cust_main_Mixin FS::Record );
 
@@ -49,7 +51,7 @@ $me = '[FS::cust_bill]';
 
 #ask FS::UID to run this stuff for us later
 FS::UID->install_callback( sub { 
 
 #ask FS::UID to run this stuff for us later
 FS::UID->install_callback( sub { 
-  $conf = new FS::Conf;
+  my $conf = new FS::Conf; #global
   $money_char       = $conf->config('money_char')       || '$';  
   $date_format      = $conf->config('date_format')      || '%x'; #/YY
   $rdate_format     = $conf->config('date_format')      || '%m/%d/%Y';  #/YYYY
   $money_char       = $conf->config('money_char')       || '$';  
   $date_format      = $conf->config('date_format')      || '%x'; #/YY
   $rdate_format     = $conf->config('date_format')      || '%m/%d/%Y';  #/YYYY
@@ -364,6 +366,7 @@ cust_bill-default_agent_invid is set and it has a value, invnum otherwise.
 
 sub display_invnum {
   my $self = shift;
 
 sub display_invnum {
   my $self = shift;
+  my $conf = $self->conf;
   if ( $conf->exists('cust_bill-default_agent_invid') && $self->agent_invid ){
     return $self->agent_invid;
   } else {
   if ( $conf->exists('cust_bill-default_agent_invid') && $self->agent_invid ){
     return $self->agent_invid;
   } else {
@@ -807,6 +810,7 @@ If there is an error, returns the error, otherwise returns false.
 
 sub apply_payments_and_credits {
   my( $self, %options ) = @_;
 
 sub apply_payments_and_credits {
   my( $self, %options ) = @_;
+  my $conf = $self->conf;
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -955,6 +959,7 @@ sub generate_email {
 
   my $self = shift;
   my %args = @_;
 
   my $self = shift;
   my %args = @_;
+  my $conf = $self->conf;
 
   my $me = '[FS::cust_bill::generate_email]';
 
 
   my $me = '[FS::cust_bill::generate_email]';
 
@@ -989,7 +994,7 @@ sub generate_email {
 
     my $alternative = build MIME::Entity
       'Type'        => 'multipart/alternative',
 
     my $alternative = build MIME::Entity
       'Type'        => 'multipart/alternative',
-      'Encoding'    => '7bit',
+      #'Encoding'    => '7bit',
       'Disposition' => 'inline'
     ;
 
       'Disposition' => 'inline'
     ;
 
@@ -1017,8 +1022,8 @@ sub generate_email {
 
     $alternative->attach(
       'Type'        => 'text/plain',
 
     $alternative->attach(
       'Type'        => 'text/plain',
-      #'Encoding'    => 'quoted-printable',
-      'Encoding'    => '7bit',
+      'Encoding'    => 'quoted-printable',
+      #'Encoding'    => '7bit',
       'Data'        => $data,
       'Disposition' => 'inline',
     );
       'Data'        => $data,
       'Disposition' => 'inline',
     );
@@ -1240,6 +1245,7 @@ sub queueable_send {
 
 sub send {
   my $self = shift;
 
 sub send {
   my $self = shift;
+  my $conf = $self->conf;
 
   my( $template, $invoice_from, $notice_name );
   my $agentnums = '';
 
   my( $template, $invoice_from, $notice_name );
   my $agentnums = '';
@@ -1329,6 +1335,7 @@ sub queueable_email {
 #sub email_invoice {
 sub email {
   my $self = shift;
 #sub email_invoice {
 sub email {
   my $self = shift;
+  my $conf = $self->conf;
 
   my( $template, $invoice_from, $notice_name, $no_coupon );
   if ( ref($_[0]) ) {
 
   my( $template, $invoice_from, $notice_name, $no_coupon );
   if ( ref($_[0]) ) {
@@ -1378,6 +1385,7 @@ sub email {
 
 sub email_subject {
   my $self = shift;
 
 sub email_subject {
   my $self = shift;
+  my $conf = $self->conf;
 
   #my $template = scalar(@_) ? shift : '';
   #per-template?
 
   #my $template = scalar(@_) ? shift : '';
   #per-template?
@@ -1409,6 +1417,7 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume
 
 sub lpr_data {
   my $self = shift;
 
 sub lpr_data {
   my $self = shift;
+  my $conf = $self->conf;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
@@ -1444,6 +1453,7 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume
 #sub print_invoice {
 sub print {
   my $self = shift;
 #sub print_invoice {
 sub print {
   my $self = shift;
+  my $conf = $self->conf;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
@@ -1483,6 +1493,7 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume
 
 sub fax_invoice {
   my $self = shift;
 
 sub fax_invoice {
   my $self = shift;
+  my $conf = $self->conf;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
   my( $template, $notice_name );
   if ( ref($_[0]) ) {
     my $opt = shift;
@@ -1538,6 +1549,7 @@ enabled)
 
 sub get_open_bill_batch {
   my $self = shift;
 
 sub get_open_bill_batch {
   my $self = shift;
+  my $conf = $self->conf;
   my $hashref = { status => 'O' };
   $hashref->{'agentnum'} = $conf->exists('invoice_print_pdf-spoolagent')
                              ? $self->cust_main->agentnum
   my $hashref = { status => 'O' };
   $hashref->{'agentnum'} = $conf->exists('invoice_print_pdf-spoolagent')
                              ? $self->cust_main->agentnum
@@ -1560,6 +1572,7 @@ TEMPLATENAME is unused?
 
 sub ftp_invoice {
   my $self = shift;
 
 sub ftp_invoice {
   my $self = shift;
+  my $conf = $self->conf;
   my $template = scalar(@_) ? shift : '';
 
   $self->send_csv(
   my $template = scalar(@_) ? shift : '';
 
   $self->send_csv(
@@ -1582,6 +1595,7 @@ TEMPLATENAME is unused?
 
 sub spool_invoice {
   my $self = shift;
 
 sub spool_invoice {
   my $self = shift;
+  my $conf = $self->conf;
   my $template = scalar(@_) ? shift : '';
 
   $self->spool_csv(
   my $template = scalar(@_) ? shift : '';
 
   $self->spool_csv(
@@ -2091,6 +2105,7 @@ sub realtime_lec {
 
 sub realtime_bop {
   my( $self, $method ) = (shift,shift);
 
 sub realtime_bop {
   my( $self, $method ) = (shift,shift);
+  my $conf = $self->conf;
   my %opt = @_;
 
   my $cust_main = $self->cust_main;
   my %opt = @_;
 
   my $cust_main = $self->cust_main;
@@ -2219,6 +2234,7 @@ I<notice_name>, if specified, overrides "Invoice" as the name of the sent docume
 
 sub print_latex {
   my $self = shift;
 
 sub print_latex {
   my $self = shift;
+  my $conf = $self->conf;
   my( $today, $template, %opt );
   if ( ref($_[0]) ) {
     %opt = %{ shift() };
   my( $today, $template, %opt );
   if ( ref($_[0]) ) {
     %opt = %{ shift() };
@@ -2284,6 +2300,7 @@ sub print_latex {
                            SUFFIX   => '.tex',
                            UNLINK   => 0,
                          ) or die "can't open temp file: $!\n";
                            SUFFIX   => '.tex',
                            UNLINK   => 0,
                          ) or die "can't open temp file: $!\n";
+  binmode($fh, ':utf8'); # language support
   print $fh join('', @filled_in );
   close $fh;
 
   print $fh join('', @filled_in );
   close $fh;
 
@@ -2352,6 +2369,7 @@ notice_name - overrides "Invoice" as the name of the sent document (templates fr
 # yes: fixed width (dot matrix) text printing will be borked
 sub print_generic {
   my( $self, %params ) = @_;
 # yes: fixed width (dot matrix) text printing will be borked
 sub print_generic {
   my( $self, %params ) = @_;
+  my $conf = $self->conf;
   my $today = $params{today} ? $params{today} : time;
   warn "$me print_generic called on $self with suffix $params{template}\n"
     if $DEBUG;
   my $today = $params{today} ? $params{today} : time;
   warn "$me print_generic called on $self with suffix $params{template}\n"
     if $DEBUG;
@@ -2635,7 +2653,11 @@ sub print_generic {
     'total_pages'     => 1,
 
   );
     'total_pages'     => 1,
 
   );
-  
+  #localization
+  my $lh = FS::L10N->get_handle($cust_main->locale);
+  $invoice_data{'emt'} = sub { &$escape_function($self->mt(@_)) };
+
   my $min_sdate = 999999999999;
   my $max_edate = 0;
   foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
   my $min_sdate = 999999999999;
   my $max_edate = 0;
   foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
@@ -2770,9 +2792,9 @@ sub print_generic {
       if ($format eq 'latex');
   }
 
       if ($format eq 'latex');
   }
 
-  $invoice_data{'po_line'} =
+  $invoice_data{'po_line'} = 
     (  $cust_main->payby eq 'BILL' && $cust_main->payinfo )
     (  $cust_main->payby eq 'BILL' && $cust_main->payinfo )
-      ? &$escape_function("Purchase Order #". $cust_main->payinfo)
+      ? &$escape_function($self->mt("Purchase Order #").$cust_main->payinfo)
       : $nbsp;
 
   my %money_chars = ( 'latex'    => '',
       : $nbsp;
 
   my %money_chars = ( 'latex'    => '',
@@ -2801,7 +2823,7 @@ sub print_generic {
   warn "$me generating sections\n"
     if $DEBUG > 1;
 
   warn "$me generating sections\n"
     if $DEBUG > 1;
 
-  my $previous_section = { 'description' => 'Previous Charges',
+  my $previous_section = { 'description' => $self->mt('Previous Charges'),
                            'subtotal'    => $other_money_char.
                                             sprintf('%.2f', $pr_total),
                            'summarized'  => $summarypage ? 'Y' : '',
                            'subtotal'    => $other_money_char.
                                             sprintf('%.2f', $pr_total),
                            'summarized'  => $summarypage ? 'Y' : '',
@@ -2813,7 +2835,7 @@ sub print_generic {
     if $conf->exists('invoice_include_aging');
 
   my $taxtotal = 0;
     if $conf->exists('invoice_include_aging');
 
   my $taxtotal = 0;
-  my $tax_section = { 'description' => 'Taxes, Surcharges, and Fees',
+  my $tax_section = { 'description' => $self->mt('Taxes, Surcharges, and Fees'),
                       'subtotal'    => $taxtotal,   # adjusted below
                       'summarized'  => $summarypage ? 'Y' : '',
                     };
                       'subtotal'    => $taxtotal,   # adjusted below
                       'summarized'  => $summarypage ? 'Y' : '',
                     };
@@ -2825,7 +2847,8 @@ sub print_generic {
 
 
   my $adjusttotal = 0;
 
 
   my $adjusttotal = 0;
-  my $adjust_section = { 'description' => 'Credits, Payments, and Adjustments',
+  my $adjust_section = { 'description' => 
+    $self->mt('Credits, Payments, and Adjustments'),
                          'subtotal'    => 0,   # adjusted below
                          'summarized'  => $summarypage ? 'Y' : '',
                        };
                          'subtotal'    => 0,   # adjusted below
                          'summarized'  => $summarypage ? 'Y' : '',
                        };
@@ -2910,7 +2933,7 @@ sub print_generic {
   
   if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
     push @buf, ['','-----------'];
   
   if ( @pr_cust_bill && !$conf->exists('disable_previous_balance') ) {
     push @buf, ['','-----------'];
-    push @buf, [ 'Total Previous Balance',
+    push @buf, [ $self->mt('Total Previous Balance'),
                  $money_char. sprintf("%10.2f", $pr_total) ];
     push @buf, ['',''];
   }
                  $money_char. sprintf("%10.2f", $pr_total) ];
     push @buf, ['',''];
   }
@@ -3066,7 +3089,7 @@ sub print_generic {
   
   if ( $taxtotal ) {
     my $total = {};
   
   if ( $taxtotal ) {
     my $total = {};
-    $total->{'total_item'} = 'Sub-total';
+    $total->{'total_item'} = $self->mt('Sub-total');
     $total->{'total_amount'} =
       $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
 
     $total->{'total_amount'} =
       $other_money_char. sprintf('%.2f', $self->charged - $taxtotal );
 
@@ -3083,7 +3106,8 @@ sub print_generic {
   $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
 
   push @buf,['','-----------'];
   $invoice_data{'taxtotal'} = sprintf('%.2f', $taxtotal);
 
   push @buf,['','-----------'];
-  push @buf,[( $conf->exists('disable_previous_balance') 
+  push @buf,[$self->mt( 
+              $conf->exists('disable_previous_balance') 
                ? 'Total Charges'
                : 'Total New Charges'
              ),
                ? 'Total Charges'
                : 'Total New Charges'
              ),
@@ -3092,7 +3116,7 @@ sub print_generic {
 
   {
     my $total = {};
 
   {
     my $total = {};
-    my $item = 'Total';
+    my $item = $self->mt('Total');
     $item = $conf->config('previous_balance-exclude_from_total')
          || 'Total New Charges'
       if $conf->exists('previous_balance-exclude_from_total');
     $item = $conf->config('previous_balance-exclude_from_total')
          || 'Total New Charges'
       if $conf->exists('previous_balance-exclude_from_total');
@@ -3107,11 +3131,11 @@ sub print_generic {
       &$embolden_function( $other_money_char.  sprintf( '%.2f', $amount ) );
     if ( $multisection ) {
       if ( $adjust_section->{'sort_weight'} ) {
       &$embolden_function( $other_money_char.  sprintf( '%.2f', $amount ) );
     if ( $multisection ) {
       if ( $adjust_section->{'sort_weight'} ) {
-        $adjust_section->{'posttotal'} = 'Balance Forward '. $other_money_char.
-          sprintf("%.2f", ($self->billing_balance || 0) );
+        $adjust_section->{'posttotal'} = $self->mt('Balance Forward').' '.
+          $other_money_char.  sprintf("%.2f", ($self->billing_balance || 0) );
       } else {
       } else {
-        $adjust_section->{'pretotal'} = 'New charges total '. $other_money_char.
-                                        sprintf('%.2f', $self->charged );
+        $adjust_section->{'pretotal'} = $self->mt('New charges total').' '.
+          $other_money_char.  sprintf('%.2f', $self->charged );
       } 
     }else{
       push @total_items, $total;
       } 
     }else{
       push @total_items, $total;
@@ -3316,25 +3340,24 @@ sub print_generic {
     }
 
     #setup subroutine for the template
     }
 
     #setup subroutine for the template
-    sub FS::cust_bill::_template::invoice_lines {
-      my $lines = shift || scalar(@FS::cust_bill::_template::buf);
+    #sub FS::cust_bill::_template::invoice_lines { # good god, no
+    $invoice_data{invoice_lines} = sub { # much better
+      my $lines = shift || scalar(@buf);
       map { 
       map { 
-        scalar(@FS::cust_bill::_template::buf)
-          ? shift @FS::cust_bill::_template::buf
+        scalar(@buf)
+          ? shift @buf
           : [ '', '' ];
       }
       ( 1 .. $lines );
           : [ '', '' ];
       }
       ( 1 .. $lines );
-    }
+    };
 
     my $lines;
     my @collect;
     while (@buf) {
       push @collect, split("\n",
 
     my $lines;
     my @collect;
     while (@buf) {
       push @collect, split("\n",
-        $text_template->fill_in( HASH => \%invoice_data,
-                                 PACKAGE => 'FS::cust_bill::_template'
-                               )
+        $text_template->fill_in( HASH => \%invoice_data )
       );
       );
-      $FS::cust_bill::_template::page++;
+      $invoice_data{'page'}++;
     }
     map "$_\n", @collect;
   }else{
     }
     map "$_\n", @collect;
   }else{
@@ -3546,6 +3569,7 @@ sub _translate_old_latex_format {
 
 sub terms {
   my $self = shift;
 
 sub terms {
   my $self = shift;
+  my $conf = $self->conf;
 
   #check for an invoice-specific override
   return $self->invoice_terms if $self->invoice_terms;
 
   #check for an invoice-specific override
   return $self->invoice_terms if $self->invoice_terms;
@@ -3574,10 +3598,11 @@ sub due_date2str {
 
 sub balance_due_msg {
   my $self = shift;
 
 sub balance_due_msg {
   my $self = shift;
-  my $msg = 'Balance Due';
+  my $msg = $self->mt('Balance Due');
   return $msg unless $self->terms;
   if ( $self->due_date ) {
   return $msg unless $self->terms;
   if ( $self->due_date ) {
-    $msg .= ' - Please pay by '. $self->due_date2str($date_format);
+    $msg .= ' - ' . $self->mt('Please pay by'). ' '.
+      $self->due_date2str($date_format);
   } elsif ( $self->terms ) {
     $msg .= ' - '. $self->terms;
   }
   } elsif ( $self->terms ) {
     $msg .= ' - '. $self->terms;
   }
@@ -3586,6 +3611,7 @@ sub balance_due_msg {
 
 sub balance_due_date {
   my $self = shift;
 
 sub balance_due_date {
   my $self = shift;
+  my $conf = $self->conf;
   my $duedate = '';
   if (    $conf->exists('invoice_default_terms') 
        && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
   my $duedate = '';
   if (    $conf->exists('invoice_default_terms') 
        && $conf->config('invoice_default_terms')=~ /^\s*Net\s*(\d+)\s*$/ ) {
@@ -3594,7 +3620,10 @@ sub balance_due_date {
   $duedate;
 }
 
   $duedate;
 }
 
-sub credit_balance_msg { 'Credit Balance Remaining' }
+sub credit_balance_msg { 
+  my $self = shift;
+  $self->mt('Credit Balance Remaining')
+}
 
 =item invnum_date_pretty
 
 
 =item invnum_date_pretty
 
@@ -3605,7 +3634,7 @@ Returns a string with the invoice number and date, for example:
 
 sub invnum_date_pretty {
   my $self = shift;
 
 sub invnum_date_pretty {
   my $self = shift;
-  'Invoice #'. $self->invnum. ' ('. $self->_date_pretty. ')';
+  $self->mt('Invoice #'). $self->invnum. ' ('. $self->_date_pretty. ')';
 }
 
 =item _date_pretty
 }
 
 =item _date_pretty
@@ -4011,6 +4040,7 @@ sub _condensed_total_line_generator {
 
 sub _items_extra_usage_sections {
   my $self = shift;
 
 sub _items_extra_usage_sections {
   my $self = shift;
+  my $conf = $self->conf;
   my $escape = shift;
   my $format = shift;
 
   my $escape = shift;
   my $format = shift;
 
@@ -4250,6 +4280,7 @@ sub _items_accountcode_cdr {
 
 sub _items_svc_phone_sections {
   my $self = shift;
 
 sub _items_svc_phone_sections {
   my $self = shift;
+  my $conf = $self->conf;
   my $escape = shift;
   my $format = shift;
 
   my $escape = shift;
   my $format = shift;
 
@@ -4497,6 +4528,7 @@ sub _items {
 
 sub _items_previous {
   my $self = shift;
 
 sub _items_previous {
   my $self = shift;
+  my $conf = $self->conf;
   my $cust_main = $self->cust_main;
   my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
   my @b = ();
   my $cust_main = $self->cust_main;
   my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
   my @b = ();
@@ -4505,7 +4537,7 @@ sub _items_previous {
                ? 'due '. $_->due_date2str($date_format)
                : time2str($date_format, $_->_date);
     push @b, {
                ? 'due '. $_->due_date2str($date_format)
                : time2str($date_format, $_->_date);
     push @b, {
-      'description' => 'Previous Balance, Invoice #'. $_->invnum. " ($date)",
+      'description' => $self->mt('Previous Balance, Invoice #'). $_->invnum. " ($date)",
       #'pkgpart'     => 'N/A',
       'pkgnum'      => 'N/A',
       'amount'      => sprintf("%.2f", $_->owed),
       #'pkgpart'     => 'N/A',
       'pkgnum'      => 'N/A',
       'amount'      => sprintf("%.2f", $_->owed),
@@ -4588,6 +4620,7 @@ sub _items_tax {
 
 sub _items_cust_bill_pkg {
   my $self = shift;
 
 sub _items_cust_bill_pkg {
   my $self = shift;
+  my $conf = $self->conf;
   my $cust_bill_pkgs = shift;
   my %opt = @_;
 
   my $cust_bill_pkgs = shift;
   my %opt = @_;
 
@@ -4913,7 +4946,7 @@ sub _items_credits {
       #'description' => 'Credit ref\#'. $_->crednum.
       #                 " (". time2str("%x",$_->cust_credit->_date) .")".
       #                 $reason,
       #'description' => 'Credit ref\#'. $_->crednum.
       #                 " (". time2str("%x",$_->cust_credit->_date) .")".
       #                 $reason,
-      'description' => 'Credit applied '.
+      'description' => $self->mt('Credit applied').' '.
                        time2str($date_format,$_->cust_credit->_date). $reason,
       'amount'      => sprintf("%.2f",$_->amount),
     };
                        time2str($date_format,$_->cust_credit->_date). $reason,
       'amount'      => sprintf("%.2f",$_->amount),
     };
@@ -4933,7 +4966,7 @@ sub _items_payments {
     #something more elaborate if $_->amount ne ->cust_pay->paid ?
 
     push @b, {
     #something more elaborate if $_->amount ne ->cust_pay->paid ?
 
     push @b, {
-      'description' => "Payment received ".
+      'description' => $self->mt('Payment received').' '.
                        time2str($date_format,$_->cust_pay->_date ),
       'amount'      => sprintf("%.2f", $_->amount )
     };
                        time2str($date_format,$_->cust_pay->_date ),
       'amount'      => sprintf("%.2f", $_->amount )
     };
@@ -5159,6 +5192,7 @@ Currently only supported on PostgreSQL.
 =cut
 
 sub due_date_sql {
 =cut
 
 sub due_date_sql {
+  my $conf = new FS::Conf;
 'COALESCE(
   SUBSTRING(
     COALESCE(
 'COALESCE(
   SUBSTRING(
     COALESCE(
index 7832eca..c1f95ea 100644 (file)
@@ -68,6 +68,7 @@ use FS::banned_pay;
 use FS::cust_main_note;
 use FS::cust_attachment;
 use FS::contact;
 use FS::cust_main_note;
 use FS::cust_attachment;
 use FS::contact;
+use FS::Locales;
 
 # 1 is mostly method/subroutine entry and options
 # 2 traces progress of some operations
 
 # 1 is mostly method/subroutine entry and options
 # 2 traces progress of some operations
@@ -1692,6 +1693,7 @@ sub check {
     || $self->ut_floatn('credit_limit')
     || $self->ut_numbern('billday')
     || $self->ut_enum('edit_subject', [ '', 'Y' ] )
     || $self->ut_floatn('credit_limit')
     || $self->ut_numbern('billday')
     || $self->ut_enum('edit_subject', [ '', 'Y' ] )
+    || $self->ut_enum('locale', [ '', FS::Locales->locales ])
   ;
 
   #barf.  need message catalogs.  i18n.  etc.
   ;
 
   #barf.  need message catalogs.  i18n.  etc.
index e8e243f..d493060 100644 (file)
@@ -538,6 +538,41 @@ sub process_email_search_result {
 
 }
 
 
 }
 
+=item conf
+
+Returns a configuration handle (L<FS::Conf>) set to the customer's locale, 
+if they have one.  If not, returns an FS::Conf with no locale.
+
+=cut
+
+sub conf {
+  my $self = shift;
+  return $self->{_conf} if (ref $self and $self->{_conf});
+  my $cust_main = $self->cust_main;
+  my $conf = new FS::Conf { 
+    'locale' => ($cust_main ? $cust_main->locale : '')
+  };
+  $self->{_conf} = $conf if ref $self;
+  return $conf;
+}
+
+=item mt TEXT [, ARGS ]
+
+Localizes a text string (see L<Locale::Maketext>) for the customer's locale,
+if they have one.
+
+=cut
+
+sub mt {
+  my $self = shift;
+  return $self->{_lh}->maketext(@_) if (ref $self and $self->{_lh});
+  my $cust_main = $self->cust_main;
+  my $locale = $cust_main ? $cust_main->locale : '';
+  my $lh = FS::L10N->get_handle($locale);
+  $self->{_lh} = $lh if ref $self;
+  return $lh->maketext(@_);
+}
+
 =back
 
 =head1 BUGS
 =back
 
 =head1 BUGS
index 50af45d..a8a57c6 100644 (file)
@@ -193,7 +193,7 @@ old_ for replace operations):
   <LI><code>$pkgnum</code>
   <LI><code>$custnum</code>
   <LI>All other fields in <b>svc_acct</b> are also available.
   <LI><code>$pkgnum</code>
   <LI><code>$custnum</code>
   <LI>All other fields in <b>svc_acct</b> are also available.
-  <LI>The following fields from <b>cust_main</b> are also available (except during replace): company, address1, address2, city, state, zip, county, daytime, night, fax, otaker, agent_custid.  When used on the command line (rather than STDIN), they will be quoted for the shell already (do not add additional quotes).
+  <LI>The following fields from <b>cust_main</b> are also available (except during replace): company, address1, address2, city, state, zip, county, daytime, night, fax, otaker, agent_custid, locale.  When used on the command line (rather than STDIN), they will be quoted for the shell already (do not add additional quotes).
 </UL>
 END
 );
 </UL>
 END
 );
@@ -263,7 +263,7 @@ sub _export_command {
     {
       no strict 'refs';
       foreach my $custf (qw( company address1 address2 city state zip country
     {
       no strict 'refs';
       foreach my $custf (qw( company address1 address2 city state zip country
-                             daytime night fax otaker agent_custid
+                             daytime night fax otaker agent_custid locale
                         ))
       {
         ${$custf} = $cust_pkg->cust_main->$custf();
                         ))
       {
         ${$custf} = $cust_pkg->cust_main->$custf();
@@ -343,6 +343,7 @@ sub _export_command {
   $fax = shell_quote $fax;
   $otaker = shell_quote $otaker; 
   $agent_custid = shell_quote $agent_custid;
   $fax = shell_quote $fax;
   $otaker = shell_quote $otaker; 
   $agent_custid = shell_quote $agent_custid;
+  $locale = shell_quote $locale;
 
   my $command_string = eval(qq("$command"));
   my @ssh_cmd_args = (
 
   my $command_string = eval(qq("$command"));
   my @ssh_cmd_args = (
@@ -419,6 +420,7 @@ sub _export_replace {
     if $error;
 
   $new_agent_custid = $new_cust_main ? $new_cust_main->agent_custid : '';
     if $error;
 
   $new_agent_custid = $new_cust_main ? $new_cust_main->agent_custid : '';
+  $new_locale = $new_cust_main ? $new_cust_main->locale : '';
   $old_pkgnum = $old_cust_pkg ? $old_cust_pkg->pkgnum : '';
   $old_custnum = $old_cust_pkg ? $old_cust_pkg->custnum : '';
   $new_pkgnum = $new_cust_pkg ? $new_cust_pkg->pkgnum : '';
   $old_pkgnum = $old_cust_pkg ? $old_cust_pkg->pkgnum : '';
   $old_custnum = $old_cust_pkg ? $old_cust_pkg->custnum : '';
   $new_pkgnum = $new_cust_pkg ? $new_cust_pkg->pkgnum : '';
@@ -432,6 +434,7 @@ sub _export_replace {
   $new_crypt_password = shell_quote $new_crypt_password;
   $new_ldap_password  = shell_quote $new_ldap_password;
   $new_agent_custid = shell_quote $new_agent_custid;
   $new_crypt_password = shell_quote $new_crypt_password;
   $new_ldap_password  = shell_quote $new_ldap_password;
   $new_agent_custid = shell_quote $new_agent_custid;
+  $new_locale = shell_quote $new_locale;
 
   my $command_string = eval(qq("$command"));
 
 
   my $command_string = eval(qq("$command"));
 
index 289ada1..1d53683 100644 (file)
@@ -11,6 +11,7 @@
 .invoice_desc_more TD { font-weight: bold; font-size: 10pt }
 .invoice_extdesc TD { font-size: 8pt }
 .invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
 .invoice_desc_more TD { font-weight: bold; font-size: 10pt }
 .invoice_extdesc TD { font-size: 8pt }
 .invoice_totaldesc TD { font-size: 10pt; empty-cells: show }
+.allcaps { text-transform:uppercase }
 </STYLE>
 
 <table class="invoice" bgcolor="#ffffff" WIDTH=625 CELLSPACING=8><tr><td>
 </STYLE>
 
 <table class="invoice" bgcolor="#ffffff" WIDTH=625 CELLSPACING=8><tr><td>
         <table CLASS="invoice_headerright" cellspacing=0>
           <tr>
             <td align="center">
         <table CLASS="invoice_headerright" cellspacing=0>
           <tr>
             <td align="center">
-              Invoice&nbsp;date<BR>
+              <%= emt('Invoice date') %><BR>
               <B><%= $date %></B>
             </td>
             <td>
             </td>
             <td align="center">
               <B><%= $date %></B>
             </td>
             <td>
             </td>
             <td align="center">
-              Invoice&nbsp;#<BR>
+              <%= emt('Invoice #') %><BR>
               <B><%= $invnum %></B>
             </td>
             <td>
             </td>
             <td align="center">
               <B><%= $invnum %></B>
             </td>
             <td>
             </td>
             <td align="center">
-              Customer&nbsp;#<BR>
+              <%= emt('Customer #') %><BR>
               <B><%= $custnum %></B>
             </td>
           </tr>
           <tr>
             <th>&nbsp;</th>
               <B><%= $custnum %></B>
             </td>
           </tr>
           <tr>
             <th>&nbsp;</th>
-            <th colspan=3 align="center">
-              <FONT SIZE="+3"><%= $notice_name ? substr($notice_name, 0, 1) : 'I' %></FONT><FONT SIZE="+2"><%= $notice_name ? uc(substr($notice_name, 1)) : 'NVOICE' %></FONT>
+            <th colspan=3 align="center" class="allcaps">
+             <FONT SIZE="+3"><%= substr(emt($notice_name),0,1) %></FONT><FONT SIZE="+2"><%= substr(emt($notice_name),1) %></FONT>
             </th>
             <th>&nbsp;</th>
           </tr>
             </th>
             <th>&nbsp;</th>
           </tr>
@@ -64,7 +65,7 @@
         %>
       </td>
       <%= $ship_enable ? ('<td align="left">'.
         %>
       </td>
       <%= $ship_enable ? ('<td align="left">'.
-                          join('<BR>',grep length($_), '<b>Service Address</b>',
+                          join('<BR>',grep length($_), '<b>'.emt('Service Address').'</b>',
                                                        $ship_company,
                                                        $ship_address1,
                                                        $ship_address2,
                                                        $ship_company,
                                                        $ship_address1,
                                                        $ship_address2,
@@ -86,7 +87,7 @@
            $OUT .= qq! <img src="cust_bill-barcode.cgi?invnum=$invnum;template=$template"><br> !;
        }
     %>
            $OUT .= qq! <img src="cust_bill-barcode.cgi?invnum=$invnum;template=$template"><br> !;
        }
     %>
-        <%= $terms ? "Terms: $terms" : '' %><BR>
+        <%= $terms ? emt('Terms') . ": $terms" : '' %><BR>
         <%= $po_line %>
       </td>
     </tr>
         <%= $po_line %>
       </td>
     </tr>
         unless ($section->{'summarized'}) {
           $OUT .= '</table>' if ( $notfirst || $section->{'pretotal'} && !$summary );
           $OUT .= '<table><tr><td>';
         unless ($section->{'summarized'}) {
           $OUT .= '</table>' if ( $notfirst || $section->{'pretotal'} && !$summary );
           $OUT .= '<table><tr><td>';
-          if ($section->{'description'}) {
-            $OUT .=
-              '<p><b><font size="+1">'. uc(substr($section->{'description'},0,1)).
-              '</font><font size="+0">'. uc(substr($section->{'description'},1)).
+          my $sectionhead = $section->{'description'} || emt('Charges');
+          $OUT .=
+              '<p class="allcaps"><b><font size="+1">'. substr($sectionhead,0,1).
+              '</font><font size="+0">'. substr($sectionhead,1).
               '</font></b>'.
               '</font></b>'.
-              '<p>';
-          }else{
-            $OUT .=
-              '<p><b><font size="+1">C</font><font size="+0">HARGES</font></b>'.
-              '<p>';
-          }
-          $OUT .= '</td></tr></table>';
+              '<p>'.
+              '</td></tr></table>';
 
           $OUT .=
             '<table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">'.
 
           $OUT .=
             '<table class="invoice_longtable" CELLSPACING=0 WIDTH="100%">'.
             $OUT .= $header;
             $columncount = scalar(my @array = split /<\/th><th/i, $header);
           } else {
             $OUT .= $header;
             $columncount = scalar(my @array = split /<\/th><th/i, $header);
           } else {
-            $OUT .=  '<th align="center">Ref</th>'.
-                     '<th align="left">Description</th>'.
-                        ( $unitprices 
-                          ? '<th align="left">Unit Price</th>'.
-                            '<th align="left">Quantity</th>'
-                          : ''
-                        ). 
-                      '<th align="right">Amount</th>';
+            $OUT .=  '<th align="center">' . emt('Ref') . '</th>'.
+                     '<th align="left">' . emt('Description') . '</th>'.
+                     ( $unitprices 
+                       ? '<th align="left">' . emt('Unit Price') . '</th>'.
+                         '<th align="left">' . emt('Quantity') . '</th>'
+                        : '' ).
+                     '<th align="right">' . emt('Amount') . '</th>';
           }
             $OUT .= '</tr>';
 
           }
             $OUT .= '</tr>';
 
             } else {
               $OUT .= qq(<td align="left" style="$style"). 
                       ( $unitprices ? ' colspan=3>' : '>' ).
             } else {
               $OUT .= qq(<td align="left" style="$style"). 
                       ( $unitprices ? ' colspan=3>' : '>' ).
-                      $section->{'description'}. ' Total </td>'.
+                      $section->{'description'}. ' ' . emt('Total') . '</td>'.
                       qq(<td align="right" style="$style">).
                       $section->{'subtotal'}. '</td>';
             }
                       qq(<td align="right" style="$style">).
                       $section->{'subtotal'}. '</td>';
             }
index 10f30cf..37f59d2 100644 (file)
@@ -21,6 +21,8 @@
 \r
 \usepackage{fancyhdr,lastpage,ifthen,array,fslongtable,afterpage,caption,multirow,bigstrut}\r
 \usepackage{graphicx}                  % required for logo graphic\r
 \r
 \usepackage{fancyhdr,lastpage,ifthen,array,fslongtable,afterpage,caption,multirow,bigstrut}\r
 \usepackage{graphicx}                  % required for logo graphic\r
+\usepackage[utf8]{inputenc}             % multilanguage support\r
+\usepackage[T1]{fontenc}\r
 \r
 \addtolength{\voffset}{-0.0cm}         % top margin to top of header\r
 \addtolength{\hoffset}{-0.6cm}         % left margin on page\r
 \r
 \addtolength{\voffset}{-0.0cm}         % top margin to top of header\r
 \addtolength{\hoffset}{-0.6cm}         % left margin on page\r
   \ifthenelse{\equal{\thepage}{1}}\r
   { % First page\r
     \begin{tabular}{ccc}\r
   \ifthenelse{\equal{\thepage}{1}}\r
   { % First page\r
     \begin{tabular}{ccc}\r
-    Invoice date & Invoice \#& Customer \#\\\r
+    [@-- join(' & ', emt('Invoice date'), emt('Invoice #'), emt('Customer #') ) --@]\\\r
     \vspace{0.2cm}\r
     \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]} \\\hline\r
     \vspace{0.2cm}\r
     \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]} \\\hline\r
-    \rule{0pt}{5ex} &~~ \huge{\textsc{[@-- $notice_name || 'Invoice' --@]}} & \\\r
+    \rule{0pt}{5ex} &~~ \huge{\textsc{[@-- emt($notice_name) --@]}} & \\\r
     \vspace{-0.2cm}\r
      & & \\\hline\r
     \end{tabular}\r
     \vspace{-0.2cm}\r
      & & \\\hline\r
     \end{tabular}\r
   { % ... pages\r
     \small{\r
       \begin{tabular}{lll}\r
   { % ... pages\r
     \small{\r
       \begin{tabular}{lll}\r
-      Invoice date & Invoice \#& Customer\#\\\r
+      [@-- join(' & ', emt('Invoice date'), emt('Invoice #'), emt('Customer #') ) --@]\\\r
       \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]}\\\r
       \end{tabular}\r
     }\r
       \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]}\\\r
       \end{tabular}\r
     }\r
 \r
 \newcommand{\FSdescriptionlength} { [@-- $unitprices ? '8.2cm' : '12.8cm' --@] }\r
 \newcommand{\FSdescriptioncolumncount} { [@-- $unitprices ? '4' : '6' --@] }\r
 \r
 \newcommand{\FSdescriptionlength} { [@-- $unitprices ? '8.2cm' : '12.8cm' --@] }\r
 \newcommand{\FSdescriptioncolumncount} { [@-- $unitprices ? '4' : '6' --@] }\r
-\newcommand{\FSunitcolumns}{ [@-- $unitprices ? '\makebox[2.5cm][l]{\textbf{~~Unit Price}}&\makebox[1.4cm]{\textbf{~Quantity}}&' : '' --@] }\r
+\newcommand{\FSunitcolumns}{ [@-- \r
+  $unitprices \r
+  ? '\makebox[2.5cm][l]{\textbf{~~'.emt('Unit Price').'}}&\makebox[1.4cm]{\textbf{~'.emt('Quantity').'}}&' \r
+  : '' --@] }\r
 \r
 \newcommand{\FShead}{\r
   \hline\r
   \rule{0pt}{2.5ex}\r
   \makebox[1.4cm]{\textbf{Ref}} &\r
 \r
 \newcommand{\FShead}{\r
   \hline\r
   \rule{0pt}{2.5ex}\r
   \makebox[1.4cm]{\textbf{Ref}} &\r
-%  \makebox[2.9cm][l]{\textbf{Description}}&\r
-%  \makebox[1.4cm][l]{}&\r
-%  \makebox[1.4cm][l]{}&\r
-%  \makebox[2.5cm][l]{}&\r
-  \multicolumn{\FSdescriptioncolumncount}{l}{\makebox[\FSdescriptionlength][l]{\textbf{Description}}}&\r
+  \multicolumn{\FSdescriptioncolumncount}{l}{\makebox[\FSdescriptionlength][l]{\textbf{[@-- emt('Description') --@]}}}&\r
   \FSunitcolumns\r
   \FSunitcolumns\r
-  \makebox[1.6cm][r]{\textbf{Amount}} \\\r
+  \makebox[1.6cm][r]{\textbf{[@-- emt('Amount') --@]}} \\\r
   \hline\r
 }\r
 \r
   \hline\r
 }\r
 \r
 \begin{minipage}[t]{6.4cm}\r
 [@--\r
   if ($ship_enable) {\r
 \begin{minipage}[t]{6.4cm}\r
 [@--\r
   if ($ship_enable) {\r
-    $OUT .= '\textbf{Service Address}\\\\';\r
+    $OUT .= '\textbf{' . emt('Service Address') . '}\\\\';\r
     $OUT .= "\\addressline{$ship_company}";\r
     $OUT .= "\\addressline{$ship_address1}";\r
     $OUT .= "\\addressline{$ship_address2}";\r
     $OUT .= "\\addressline{$ship_company}";\r
     $OUT .= "\\addressline{$ship_address1}";\r
     $OUT .= "\\addressline{$ship_address2}";\r
   }\r
 --@]\r
 \begin{flushright}\r
   }\r
 --@]\r
 \begin{flushright}\r
-[@-- $terms ? "Terms: $terms" : '' --@]\\\r
+[@-- $terms ? emt('Terms') .": $terms" : '' --@]\\\r
 [@-- $po_line --@]\\\r
 \end{flushright}\r
 \end{minipage}}\r
 [@-- $po_line --@]\\\r
 \end{flushright}\r
 \end{minipage}}\r
         if $coupon;\r
       $OUT .= '\begin{longtable}{cllllllr}';\r
       $OUT .= '\caption*{ ';\r
         if $coupon;\r
       $OUT .= '\begin{longtable}{cllllllr}';\r
       $OUT .= '\caption*{ ';\r
-      $OUT .= ($section->{'description'}) ? $section->{'description'}: 'Charges';\r
+      $OUT .= ($section->{'description'}) ? $section->{'description'}: emt('Charges');\r
       $OUT .= '}\\\\';\r
       if ($section->{header_generator}) {\r
         $OUT .= &{$section->{header_generator}}();\r
       $OUT .= '}\\\\';\r
       if ($section->{header_generator}) {\r
         $OUT .= &{$section->{header_generator}}();\r
         $OUT .= '\FShead';\r
       }\r
       $OUT .= '\endfirsthead';\r
         $OUT .= '\FShead';\r
       }\r
       $OUT .= '\endfirsthead';\r
-      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\\';\r
+      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}'.emt('Continued from previous page').'}\\\\';\r
       if ($section->{header_generator}) {\r
         $OUT .= &{$section->{header_generator}}();\r
       } else {\r
         $OUT .= '\FShead';\r
       }\r
       $OUT .= '\endhead';\r
       if ($section->{header_generator}) {\r
         $OUT .= &{$section->{header_generator}}();\r
       } else {\r
         $OUT .= '\FShead';\r
       }\r
       $OUT .= '\endhead';\r
-      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}Continued on next page...}\\\\';\r
+      $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}'.emt('Continued on next page...').'}\\\\';\r
       $OUT .= '\endfoot';\r
       $OUT .= '\hline';\r
 \r
       $OUT .= '\endfoot';\r
       $OUT .= '\hline';\r
 \r
index ebf8ef7..769f043 100644 (file)
@@ -1,6 +1,6 @@
 
 
-                                 { $notice_name || 'Invoice'; }
-                                 { substr("Page $page of $total_pages          ", 0, 19); } { use Date::Format; time2str("%x", $date); }  Invoice #{ $invnum; }
+                                 { emt($notice_name) }
+                                 { substr(emt("Page [_1] of [_2]          ", $page, $total_pages), 0, 19); } { use Date::Format; time2str("%x", $date); }  { emt("Invoice #") . $invnum; }
 
 
 { $company_name; }
 
 
 { $company_name; }
index 6979246..c071f2a 100644 (file)
@@ -1,21 +1,3 @@
-%
-%
-%my $conf=new FS::Conf;
-%
-%http_header('Content-Type' => 'application/x-unknown' );
-%
-%die "No configuration variable specified (bad URL)!" # umm
-%  unless $cgi->param('key');
-%$cgi->param('key') =~  /^([-\w.]+)$/;
-%my $name = $1;
-%
-%my $agentnum;
-%if ($cgi->param('agentnum') =~ /^(\d+)$/) {
-%  $agentnum = $1;
-%}
-%
-%http_header('Content-Disposition' => "attachment; filename=$name" );
-% print $conf->config_binary($name, $agentnum);
 <%init>
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 <%init>
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
@@ -25,4 +7,24 @@ if ($cgi->param('agentnum') =~ /^(\d+)$/) {
   $agentnum = $1;
 }
 
   $agentnum = $1;
 }
 
+http_header('Content-Type' => 'application/x-unknown' );
+
+die "No configuration variable specified (bad URL)!" # umm
+  unless $cgi->param('key');
+$cgi->param('key') =~  /^([-\w.]+)$/;
+my $name = $1;
+
+my $agentnum;
+if ($cgi->param('agentnum') =~ /^(\d+)$/) {
+  $agentnum = $1;
+}
+
+my $locale = '';
+if ($cgi->param('locale') =~ /^(\w+)$/) {
+  $locale = $1;
+}
+my $conf=new FS::Conf { 'locale' => $locale };
+
+http_header('Content-Disposition' => "attachment; filename=$name" );
+print $conf->config_binary($name, $agentnum);
 </%init>
 </%init>
index 0de9d42..0e04ab5 100644 (file)
@@ -4,8 +4,6 @@
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
-my $conf = new FS::Conf;
-
 http_header( 'Content-Type' => 'image/png' ); #just png for now
 
 $cgi->param('key') =~ /^([-\w.]+)$/ or die "illegal config option";
 http_header( 'Content-Type' => 'image/png' ); #just png for now
 
 $cgi->param('key') =~ /^([-\w.]+)$/ or die "illegal config option";
@@ -16,6 +14,13 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
   $agentnum = $1;
 }
 
   $agentnum = $1;
 }
 
+my $locale = '';
+if ( $cgi->param('locale') =~ /^(\w+)$/ ) {
+  $locale = $1;
+}
+
+my $conf = new FS::Conf { 'locale' => $locale };
+
 my $logo = $conf->config_binary($name, $agentnum);
 $logo = eps2png($logo) if $name =~ /\.eps$/i;
 
 my $logo = $conf->config_binary($name, $agentnum);
 $logo = eps2png($logo) if $name =~ /\.eps$/i;
 
index 4a84c0d..c62a8c7 100644 (file)
@@ -25,7 +25,7 @@
 %  }
 %
 %} else {
 %  }
 %
 %} else {
-<% header('Configuration set') %>
+<& /elements/header.html, 'Configuration set' &>
   <SCRIPT TYPE="text/javascript">
 %   my $n = 0;
 %   foreach my $type ( ref($i->type) ? @{$i->type} : $i->type ) {
   <SCRIPT TYPE="text/javascript">
 %   my $n = 0;
 %   foreach my $type ( ref($i->type) ? @{$i->type} : $i->type ) {
@@ -103,7 +103,9 @@ my %namecol = (
 my $curuser = $FS::CurrentUser::CurrentUser;
 die "access denied\n" unless $curuser->access_right('Configuration');
 
 my $curuser = $FS::CurrentUser::CurrentUser;
 die "access denied\n" unless $curuser->access_right('Configuration');
 
-my $conf = new FS::Conf;
+my $locale = $cgi->param('locale') || '';
+
+my $conf = new FS::Conf { 'locale' => $locale };
 
 if ( $conf->exists('disable_settings_changes') ) {
   my @changers = split(/\s*,\s*/, $conf->config('disable_settings_changes'));
 
 if ( $conf->exists('disable_settings_changes') ) {
   my @changers = split(/\s*,\s*/, $conf->config('disable_settings_changes'));
index e7cadbe..5964689 100644 (file)
@@ -16,6 +16,38 @@ Click on a configuration value to change it.
 %   }
 %
 % }
 %   }
 %
 % }
+% if ( @locales ) {
+( 
+% if ( $locale ) {
+%   $cgi->delete('locale');
+    <a href="<%$cgi->self_url%>">global settings</a> | 
+% }
+<script type='text/javascript'>
+function changeLocale(what) {
+  //var what = document.getElementById('select-locale');
+  if(what.selectedIndex > 0) {
+    what.form.submit();
+  }
+}
+</script>
+invoice language options: 
+<form action="<% $cgi->self_url %>" method="GET" style="display:inline;">
+<& /elements/select.html,
+    'field' => 'locale',
+    'options' => [ '', @locales ],
+    'labels'  => { map { 
+        my %info = FS::Locales->locale_info($_);
+        $_ => "$info{name} ($info{country})"
+    } @locales },
+    'curr_value' => $locale,
+    'id' => 'select-locale',
+    'onchange' => 'changeLocale'
+    &>
+  )
+%   $cgi->param('locale', $locale);
+% }
+</form>
+
 <BR><BR>
 
 <% include('/elements/init_overlib.html') %>
 <BR><BR>
 
 <% include('/elements/init_overlib.html') %>
@@ -89,11 +121,12 @@ Click on a configuration value to change it.
 %       if $agent && $cgi->param('showagent');
 %
 %     #indentation :/
 %       if $agent && $cgi->param('showagent');
 %
 %     #indentation :/
+%     my $action = 'config.cgi?key=' . $i->key . 
+%       ";agentnum=$agentnum" . ($locale ? ";locale=$locale" : '');
 
     <tr>
       <td><% include('/elements/popup_link.html',
 
     <tr>
       <td><% include('/elements/popup_link.html',
-                       'action'      => 'config.cgi?key='.      $i->key.
-                                                  ';agentnum='. $agentnum,
+                       'action'      => $action,
                        'width'       => $width,
                        'height'      => $height,
                        'actionlabel' => 'Enter configuration value',
                        'width'       => $width,
                        'height'      => $height,
                        'actionlabel' => 'Enter configuration value',
@@ -128,12 +161,12 @@ Click on a configuration value to change it.
             </tr>
 
 %   } elsif ( $type eq 'image' ) {
             </tr>
 
 %   } elsif ( $type eq 'image' ) {
+%           my $args = 'key=' . $i->key . ";agentnum=$agentnum;locale=$locale";
 
             <tr>
               <td bgcolor='#ffffff'>
                 <% $conf->exists($i->key, $agentnum)
 
             <tr>
               <td bgcolor='#ffffff'>
                 <% $conf->exists($i->key, $agentnum)
-                     ? '<img src="config-image.cgi?key='.      $i->key.
-                                                 ';agentnum='. $agentnum. '">'
+                     ? '<img src="config-image.cgi?'.$args.'">'
                      : 'empty'
                 %>
               </td>
                      : 'empty'
                 %>
               </td>
@@ -141,18 +174,19 @@ Click on a configuration value to change it.
             <tr>
               <td>
                 <% $conf->exists($i->key, $agentnum)
             <tr>
               <td>
                 <% $conf->exists($i->key, $agentnum)
-                     ? qq!<a href="config-download.cgi?key=!. $i->key. ';agentnum='. $agentnum. qq!">download</a>!
+                     ? '<a href="config-download.cgi?'.$args.'">download</a>'
                      : ''
                 %>
               </td>
             </tr>
 
 %   } elsif ( $type eq 'binary' ) {
                      : ''
                 %>
               </td>
             </tr>
 
 %   } elsif ( $type eq 'binary' ) {
+%           my $args = 'key=' . $i->key . ";agentnum=$agentnum;locale=$locale";
 
             <tr>
               <td>
                 <% $conf->exists($i->key, $agentnum)
 
             <tr>
               <td>
                 <% $conf->exists($i->key, $agentnum)
-                     ? qq!<a href="config-download.cgi?key=!. $i->key. ';agentnum='. $agentnum. qq!">download</a>!
+                     ? '<a href="config-download.cgi?'.$args.'">download</a>'
                      : 'empty'
                 %>
               </td>
                      : 'empty'
                 %>
               </td>
@@ -344,14 +378,37 @@ if ($cgi->param('agentnum') =~ /^(\d+)$/) {
   die "Agent $page_agentnum not found!" unless $page_agent;
 
   push @menubar, 'View all agents' => $p.'browse/agent.cgi';
   die "Agent $page_agentnum not found!" unless $page_agent;
 
   push @menubar, 'View all agents' => $p.'browse/agent.cgi';
+}
+
+my $conf = new FS::Conf;
+my $conf_global = $conf;
+
+my @locales = $conf_global->config('available-locales');
+
+# if this is set, we are in locale mode, so limit the displayed items 
+# to those with per_locale.
+my $locale;
+my $locale_desc;
+if ( $cgi->param('locale') =~ /^\w+_\w+$/ ) {
+  $locale = $cgi->param('locale');
+  # and set the context on $conf
+  $conf = new FS::Conf { 'locale' => $locale, 'localeonly' => 1 };
+  my %locale_info = FS::Locales->locale_info($locale);
+  $locale_desc = "$locale_info{name} ($locale_info{country})";
+
+  $title = 'Invoice Configuration'; #for now it is only invoicing
+  $title .= ' for '.$page_agent->agent if $page_agent;
+  $title .= ', '.$locale_desc;
+
+} elsif ($page_agent) {
   $title = 'Agent Configuration for '. $page_agent->agent;
   $title = 'Agent Configuration for '. $page_agent->agent;
+  $title .= ", $locale_desc" if $locale;
 } else {
   $title = 'Global Configuration';
 }
 
 } else {
   $title = 'Global Configuration';
 }
 
-my $conf = new FS::Conf;
-my @config_items = grep { $page_agent ? $_->per_agent : 1 }
+my @config_items = grep { !defined($locale) or $_->per_locale }
+                   grep { $page_agent ? $_->per_agent : 1 }
                    grep { $page_agent ? 1 : !$_->agentonly }
                         $conf->config_items; 
 
                    grep { $page_agent ? 1 : !$_->agentonly }
                         $conf->config_items; 
 
index 040ed04..6a1eaec 100644 (file)
@@ -24,6 +24,7 @@ function SafeOnsubmit() {
 
 <FORM NAME="OneTrueForm" ACTION="config-process.cgi" METHOD="POST" enctype="multipart/form-data" onSubmit="SafeOnsubmit()">
 <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>">
 
 <FORM NAME="OneTrueForm" ACTION="config-process.cgi" METHOD="POST" enctype="multipart/form-data" onSubmit="SafeOnsubmit()">
 <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>">
+<INPUT TYPE="hidden" NAME="locale" VALUE="<% $locale %>">
 <INPUT TYPE="hidden" NAME="key" VALUE="<% $key %>">
 
 Setting <b><% $key %></b>
 <INPUT TYPE="hidden" NAME="key" VALUE="<% $key %>">
 
 Setting <b><% $key %></b>
@@ -49,7 +50,8 @@ Setting <b><% $key %></b>
   <% $conf->exists($key, $agentnum)
        ? 'Current image<br>'.
          '<img src="config-image.cgi?key='.      $key.
   <% $conf->exists($key, $agentnum)
        ? 'Current image<br>'.
          '<img src="config-image.cgi?key='.      $key.
-                                   ';agentnum='. $agentnum. '"><br>'
+                                   ';agentnum='. $agentnum.
+                                   ';locale='.   $locale .'"><br>'
        : ''
   %>
 
        : ''
   %>
 
@@ -318,10 +320,6 @@ Setting <b><% $key %></b>
 </HTML>
 <%once>
 
 </HTML>
 <%once>
 
-my $conf = new FS::Conf;
-my @config_items = $conf->config_items; 
-my %confitems = map { $_->key => $_ } @config_items;
-
 my %element_types = map { $_ => 1 } qw(
   select-part_svc select-part_pkg select-pkg_class select-agent
 );
 my %element_types = map { $_ => 1 } qw(
   select-part_svc select-part_pkg select-pkg_class select-agent
 );
@@ -339,6 +337,15 @@ if ($cgi->param('agentnum') =~ /(\d+)$/) {
   $agentnum=$1;
 }
 
   $agentnum=$1;
 }
 
+my $locale = '';
+if ( $cgi->param('locale') =~ /^(\w+_\w+)$/) {
+  $locale = $1;
+}
+
+my $conf = new FS::Conf { 'locale' => $locale, 'localeonly' => 1 };
+my @config_items = $conf->config_items; 
+my %confitems = map { $_->key => $_ } @config_items;
+
 my $agent = '';
 my $title;
 if ($agentnum) {
 my $agent = '';
 my $title;
 if ($agentnum) {
index f2d6271..294104b 100644 (file)
@@ -555,6 +555,24 @@ function toggle(obj) {
       <INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>">
 % }
 
       <INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>">
 % }
 
+%my @available_locales = $conf->config('available-locales');
+%if ( scalar(@available_locales) ) {
+%   push @available_locales, '';
+%    my %locale_labels = map { 
+%        my %ll;
+%        my %info = FS::Locales->locale_info($_);
+%        $ll{$_} = $info{name} . " (" . $info{country} . ")";
+%        %ll;
+%    } FS::Locales->locales;
+  <& /elements/tr-select.html, 
+        'label'         => emt('Invoicing locale'),
+        'field'         => 'locale',
+        'options'       => \@available_locales,
+        'labels'        => \%locale_labels,
+        'curr_value'    => $cust_main->locale,
+  &>
+% }
+
   </TABLE>
 
   <% $r %> <% mt('required fields') |h %> 
   </TABLE>
 
   <% $r %> <% mt('required fields') |h %> 
index ad2ff54..75321ef 100755 (executable)
@@ -5,7 +5,7 @@ die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('View invoices')
       or $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
   unless $FS::CurrentUser::CurrentUser->access_right('View invoices')
       or $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
-my $conf = new FS::Conf;
+my $conf;
 
 my $templatename;
 my $agentnum = '';
 
 my $templatename;
 my $agentnum = '';
@@ -13,6 +13,7 @@ if ( $cgi->param('invnum') ) {
   $templatename = $cgi->param('template') || $cgi->param('templatename');
   my $cust_bill = qsearchs('cust_bill', { 'invnum' => $cgi->param('invnum') } )
     or die 'unknown invnum';
   $templatename = $cgi->param('template') || $cgi->param('templatename');
   my $cust_bill = qsearchs('cust_bill', { 'invnum' => $cgi->param('invnum') } )
     or die 'unknown invnum';
+  $conf = $cust_bill->conf;
   $agentnum = $cust_bill->cust_main->agentnum;
 } else {
   my($query) = $cgi->keywords;
   $agentnum = $cust_bill->cust_main->agentnum;
 } else {
   my($query) = $cgi->keywords;
index cf22ff7..09c0b4d 100644 (file)
   </TR>
 % }
 
   </TR>
 % }
 
+% if ( $cust_main->locale ) {
+% my %locale_info = FS::Locales->locale_info($cust_main->locale);
+  <TR>
+    <TD ALIGN="right"><% mt('Invoicing locale') |h %></TD>
+    <TD BGCOLOR="#ffffff"><% $locale_info{name} . " (" . $locale_info{country} .")" %></TD>
+  </TR>
+% }
+
+
 </TABLE></TD></TR></TABLE>
 <%once>
 
 </TABLE></TD></TR></TABLE>
 <%once>