summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2013-06-10 23:15:03 -0700
committerIvan Kohler <ivan@freeside.biz>2013-06-10 23:15:03 -0700
commitb7a2175dd9b386441f4ab66869d73083e5e8beb1 (patch)
tree3d96ed11d9ec68a9becd2e5b38548974a7a98e0e
parente497261817ee2cf3acb5ee3dda3c5906f1c13a4f (diff)
multi-currency, RT#21565
-rw-r--r--FS/FS/Schema.pm38
-rw-r--r--FS/FS/cust_bill_pkg.pm6
-rw-r--r--FS/FS/cust_main.pm2
-rw-r--r--FS/FS/cust_main/Billing.pm43
-rw-r--r--FS/FS/cust_pkg.pm24
-rw-r--r--FS/FS/part_pkg/currency_fixed.pm40
-rw-r--r--FS/FS/part_pkg/flat.pm4
-rw-r--r--FS/FS/part_pkg/recur_Common.pm2
-rw-r--r--httemplate/edit/cust_main/billing.html17
-rw-r--r--httemplate/search/cust_bill_pkg.cgi28
-rw-r--r--httemplate/view/cust_main/billing.html7
11 files changed, 165 insertions, 46 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index f1cc09c02..da3ddab99 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -768,24 +768,26 @@ sub tables_hashref {
'cust_bill_pkg' => {
'columns' => [
- 'billpkgnum', 'serial', '', '', '', '',
- 'invnum', 'int', '', '', '', '',
- 'pkgnum', 'int', '', '', '', '',
- 'pkgpart_override', 'int', 'NULL', '', '', '',
- 'setup', @money_type, '', '',
- 'recur', @money_type, '', '',
- #XXX a currency for a line item? or just one for the entire invoice
- #'currency', 'char', 'NULL', 3, '', '',
- 'sdate', @date_type, '', '',
- 'edate', @date_type, '', '',
- 'itemdesc', 'varchar', 'NULL', $char_d, '', '',
- 'itemcomment', 'varchar', 'NULL', $char_d, '', '',
- 'section', 'varchar', 'NULL', $char_d, '', '',
- 'freq', 'varchar', 'NULL', $char_d, '', '',
- 'quantity', 'int', 'NULL', '', '', '',
- 'unitsetup', @money_typen, '', '',
- 'unitrecur', @money_typen, '', '',
- 'hidden', 'char', 'NULL', 1, '', '',
+ 'billpkgnum', 'serial', '', '', '', '',
+ 'invnum', 'int', '', '', '', '',
+ 'pkgnum', 'int', '', '', '', '',
+ 'pkgpart_override', 'int', 'NULL', '', '', '',
+ 'setup', @money_type, '', '',
+ 'unitsetup', @money_typen, '', '',
+ 'setup_billed_currency', 'char', 'NULL', 3, '', '',
+ 'setup_billed_amount', @money_typen, '', '',
+ 'recur', @money_type, '', '',
+ 'unitrecur', @money_typen, '', '',
+ 'recur_billed_currency', 'char', 'NULL', 3, '', '',
+ 'recur_billed_amount', @money_typen, '', '',
+ 'sdate', @date_type, '', '',
+ 'edate', @date_type, '', '',
+ 'itemdesc', 'varchar', 'NULL', $char_d, '', '',
+ 'itemcomment', 'varchar', 'NULL', $char_d, '', '',
+ 'section', 'varchar', 'NULL', $char_d, '', '',
+ 'freq', 'varchar', 'NULL', $char_d, '', '',
+ 'quantity', 'int', 'NULL', '', '', '',
+ 'hidden', 'char', 'NULL', 1, '', '',
],
'primary_key' => 'billpkgnum',
'unique' => [],
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 0c8c0bbbf..572fe7973 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -434,7 +434,13 @@ sub check {
|| $self->ut_snumber('pkgnum')
|| $self->ut_number('invnum')
|| $self->ut_money('setup')
+ || $self->ut_moneyn('unitsetup')
+ || $self->ut_currencyn('setup_billed_currency')
+ || $self->ut_moneyn('setup_billed_amount')
|| $self->ut_money('recur')
+ || $self->ut_moneyn('unitrecur')
+ || $self->ut_currencyn('recur_billed_currency')
+ || $self->ut_moneyn('recur_billed_amount')
|| $self->ut_numbern('sdate')
|| $self->ut_numbern('edate')
|| $self->ut_textn('itemdesc')
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 3fbee1594..f3122aaa2 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -1757,7 +1757,7 @@ sub check {
|| $self->ut_flag('invoice_noemail')
|| $self->ut_flag('message_noemail')
|| $self->ut_enum('locale', [ '', FS::Locales->locales ])
- || $self->ut_currency('currency')
+ || $self->ut_currencyn('currency')
;
my $company = $self->company;
diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm
index 814802b34..220f66a0c 100644
--- a/FS/FS/cust_main/Billing.pm
+++ b/FS/FS/cust_main/Billing.pm
@@ -951,6 +951,8 @@ sub _make_lines {
my $unitsetup = 0;
my @setup_discounts = ();
my %setup_param = ( 'discounts' => \@setup_discounts );
+ my $setup_billed_currency = '';
+ my $setup_billed_amount = 0;
if ( ! $options{recurring_only}
and ! $options{cancel}
and ( $options{'resetup'}
@@ -977,7 +979,13 @@ sub _make_lines {
return "$@ running calc_setup for $cust_pkg\n"
if $@;
- $unitsetup = $cust_pkg->part_pkg->unit_setup || $setup; #XXX uuh
+ $unitsetup = $cust_pkg->base_setup()
+ || $setup; #XXX uuh
+
+ if ( $setup_param{'billed_currency'} ) {
+ $setup_billed_currency = delete $setup_param{'billed_currency'};
+ $setup_billed_amount = delete $setup_param{'billed_amount'};
+ }
}
$cust_pkg->setfield('setup', $time)
@@ -997,6 +1005,8 @@ sub _make_lines {
my $recur = 0;
my $unitrecur = 0;
my @recur_discounts = ();
+ my $recur_billed_currency = '';
+ my $recur_billed_amount = 0;
my $sdate;
if ( ! $cust_pkg->start_date
and ( ! $cust_pkg->susp || $cust_pkg->option('suspend_bill',1)
@@ -1058,6 +1068,11 @@ sub _make_lines {
#base_cancel???
$unitrecur = $cust_pkg->base_recur( \$sdate ) || $recur; #XXX uuh, better
+ if ( $param{'billed_currency'} ) {
+ $recur_billed_currency = delete $param{'billed_currency'};
+ $recur_billed_amount = delete $param{'billed_amount'};
+ }
+
if ( $increment_next_bill ) {
my $next_bill;
@@ -1173,16 +1188,20 @@ sub _make_lines {
push @details, @cust_pkg_detail;
my $cust_bill_pkg = new FS::cust_bill_pkg {
- 'pkgnum' => $cust_pkg->pkgnum,
- 'setup' => $setup,
- 'unitsetup' => $unitsetup,
- 'recur' => $recur,
- 'unitrecur' => $unitrecur,
- 'quantity' => $cust_pkg->quantity,
- 'details' => \@details,
- 'discounts' => [ @setup_discounts, @recur_discounts ],
- 'hidden' => $part_pkg->hidden,
- 'freq' => $part_pkg->freq,
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'setup' => $setup,
+ 'unitsetup' => $unitsetup,
+ 'setup_billed_currency' => $setup_billed_currency,
+ 'setup_billed_amount' => $setup_billed_amount,
+ 'recur' => $recur,
+ 'unitrecur' => $unitrecur,
+ 'recur_billed_currency' => $recur_billed_currency,
+ 'recur_billed_amount' => $recur_billed_amount,
+ 'quantity' => $cust_pkg->quantity,
+ 'details' => \@details,
+ 'discounts' => [ @setup_discounts, @recur_discounts ],
+ 'hidden' => $part_pkg->hidden,
+ 'freq' => $part_pkg->freq,
};
if ( $part_pkg->option('prorate_defer_bill',1)
@@ -1194,7 +1213,7 @@ sub _make_lines {
$cust_bill_pkg->sdate( $hash{last_bill} );
$cust_bill_pkg->edate( $sdate - 86399 ); #60s*60m*24h-1
$cust_bill_pkg->edate( $time ) if $options{cancel};
- } else { #if ( $part_pkg->recur_temporality eq 'upcoming' ) {
+ } else { #if ( $part_pkg->recur_temporality eq 'upcoming' )
$cust_bill_pkg->sdate( $sdate );
$cust_bill_pkg->edate( $cust_pkg->bill );
#$cust_bill_pkg->edate( $time ) if $options{cancel};
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 835738604..3d24ea5b2 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -2191,6 +2191,18 @@ sub calc_recur {
$self->part_pkg->calc_recur($self, @_);
}
+=item base_setup
+
+Calls the I<base_setup> of the FS::part_pkg object associated with this billing
+item.
+
+=cut
+
+sub base_setup {
+ my $self = shift;
+ $self->part_pkg->base_setup($self, @_);
+}
+
=item base_recur
Calls the I<base_recur> of the FS::part_pkg object associated with this billing
@@ -2345,9 +2357,11 @@ sub num_cust_event {
=item part_pkg_currency_option OPTIONNAME
-Returns the option value for the given name and the currency of this customer
-(see L<FS::part_pkg_currency>). If this customer has no currency, returns
-the regular option value for the given name (see L<FS::part_pkg_option>).
+Returns a two item list consisting of the currency of this customer, if any,
+and a value for the provided option. If the customer has a currency, the value
+is the option value the given name and the currency (see
+L<FS::part_pkg_currency>). Otherwise, if the customer has no currency, is the
+regular option value for the given name (see L<FS::part_pkg_option>).
=cut
@@ -2355,9 +2369,9 @@ sub part_pkg_currency_option {
my( $self, $optionname ) = @_;
my $part_pkg = $self->part_pkg;
if ( my $currency = $self->cust_main->currency ) {
- $part_pkg->part_pkg_currency_option($currency, $optionname);
+ ($currency, $part_pkg->part_pkg_currency_option($currency, $optionname) );
} else {
- $part_pkg->option($optionname);
+ ('', $part_pkg->option($optionname) );
}
}
diff --git a/FS/FS/part_pkg/currency_fixed.pm b/FS/FS/part_pkg/currency_fixed.pm
index bee6c5b4e..ce7145278 100644
--- a/FS/FS/part_pkg/currency_fixed.pm
+++ b/FS/FS/part_pkg/currency_fixed.pm
@@ -5,7 +5,8 @@ use base qw( FS::part_pkg::recur_Common );
use strict;
use vars qw( %info );
-#use FS::Record qw(qsearch qsearchs);
+use FS::Record qw(qsearchs); # qsearch qsearchs);
+use FS::currency_exchange;
%info = (
'name' => 'Per-currency pricing from package definitions',
@@ -26,7 +27,7 @@ use vars qw( %info );
},
'fieldorder' => [qw( recur_method cutoff_day ),
FS::part_pkg::prorate_Mixin::fieldorder,
- )],
+ ],
'weight' => '59',
);
@@ -37,19 +38,44 @@ sub price_info {
$str;
}
-#some false laziness w/recur_Common, could have been better about it.. pry when
-# we do discounting
+sub base_setup {
+ my($self, $cust_pkg, $sdate, $details, $param ) = @_;
+
+ $self->calc_currency_option('setup_fee', $cust_pkg, $sdate, $details, $param);
+}
+
sub calc_setup {
my($self, $cust_pkg, $sdate, $details, $param) = @_;
return 0 if $self->prorate_setup($cust_pkg, $sdate);
- sprintf('%.2f', $cust_pkg->part_pkg_currency_option('setup_fee') );
+ $self->base_setup($cust_pkg, $sdate, $details, $param);
+}
+
+use FS::Conf;
+sub calc_currency_option {
+ my($self, $optionname, $cust_pkg, $sdate, $details, $param) = @_;
+
+ my($currency, $amount) = $cust_pkg->part_pkg_currency_option($optionname);
+ return sprintf('%.2f', $amount ) unless $currency;
+
+ $param->{'billed_currency'} = $currency;
+ $param->{'billed_amount'} = $amount;
+
+ my $currency_exchange = qsearchs('currency_exchange', {
+ 'from_currency' => $currency,
+ 'to_currency' => ( FS::Conf->new->config('currency') || 'USD' ),
+ }) or die "No exchange rate from $currency\n";
+
+ #XXX do we want the rounding here to work differently?
+ #my $recognized_amount =
+ sprintf('%.2f', $amount * $currency_exchange->rate);
}
sub base_recur {
- my( $self, $cust_pkg ) = @_;
- sprintf('%.2f', $cust_pkg->part_pkg_currency_option('recur_fee') );
+ my( $self, $cust_pkg, $sdate, $details, $param ) = @_;
+ $param ||= {};
+ $self->calc_currency_option('recur_fee', $cust_pkg, $sdate, $details, $param);
}
sub can_discount { 0; } #can't discount yet (percentage would work, but amount?)
diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
index 22eb69815..9737a94c0 100644
--- a/FS/FS/part_pkg/flat.pm
+++ b/FS/FS/part_pkg/flat.pm
@@ -122,7 +122,7 @@ sub calc_setup {
my $quantity = $cust_pkg->quantity || 1;
- my $charge = $quantity * $self->unit_setup($cust_pkg, $sdate, $details);
+ my $charge = $quantity * $self->base_setup($cust_pkg, $sdate, $details);
my $discount = 0;
if ( $charge > 0 ) {
@@ -134,7 +134,7 @@ sub calc_setup {
sprintf('%.2f', $charge - $discount);
}
-sub unit_setup {
+sub base_setup {
my($self, $cust_pkg, $sdate, $details ) = @_;
$self->option('setup_fee') || 0;
diff --git a/FS/FS/part_pkg/recur_Common.pm b/FS/FS/part_pkg/recur_Common.pm
index 03d5c2cb2..ebf8869f6 100644
--- a/FS/FS/part_pkg/recur_Common.pm
+++ b/FS/FS/part_pkg/recur_Common.pm
@@ -61,7 +61,7 @@ sub calc_recur_Common {
my $recur_method = $self->option('recur_method', 1) || 'anniversary';
my @cutoff_day = $self->cutoff_day($cust_pkg);
- $charges = $self->base_recur($cust_pkg);
+ $charges = $self->base_recur($cust_pkg, $sdate, $details, $param);
$charges += $param->{'override_charges'} if $param->{'override_charges'};
if ( $recur_method eq 'prorate' ) {
diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html
index 5a66f0a60..da5f0f27f 100644
--- a/httemplate/edit/cust_main/billing.html
+++ b/httemplate/edit/cust_main/billing.html
@@ -615,6 +615,23 @@ function toggle(obj) {
<INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>">
% }
+%my @currencies = $conf->config('currencies');
+%if ( scalar(@currencies) ) {
+% unshift @currencies, ''; #default
+% my %currency_labels = map { $_ => "$_: ". code2currency($_) } @currencies;
+% $currency_labels{''} =
+% 'Default: '. code2currency( $conf->config('currency') || 'USD' );
+
+ <& /elements/tr-select.html,
+ 'label' => emt('Invoicing currency'),
+ 'field' => 'currency',
+ 'options' => \@currencies,
+ 'labels' => \%currency_labels,
+ 'curr_value' => $cust_main->currency,
+ &>
+% }
+
+
%my @available_locales = $conf->config('available-locales');
%if ( scalar(@available_locales) ) {
% push @available_locales, ''
diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi
index 183051170..b2ff45b09 100644
--- a/httemplate/search/cust_bill_pkg.cgi
+++ b/httemplate/search/cust_bill_pkg.cgi
@@ -10,6 +10,7 @@
emt('Description'),
@post_desc_header,
@peritem_desc,
+ @currency_desc,
emt('Invoice'),
emt('Date'),
emt('Paid'),
@@ -32,6 +33,7 @@
#strikethrough or "N/A ($amount)" or something these when
# they're not applicable to pkg_tax search
@peritem_sub,
+ @currency_sub,
'invnum',
sub { time2str('%b %d %Y', shift->_date ) },
sub { sprintf($money_char.'%.2f', shift->get('pay_amount')) },
@@ -44,6 +46,7 @@
'',
@post_desc_null,
@peritem,
+ @currency,
'invnum',
'_date',
#'pay_amount',
@@ -55,6 +58,7 @@
'',
@post_desc_null,
@peritem_null,
+ @currency_null,
$ilink,
$ilink,
$pay_link,
@@ -68,6 +72,7 @@
'rl'.
$post_desc_align.
$peritem_align.
+ $currency_align.
'rcrr'.
FS::UI::Web::cust_aligns(),
'color' => [
@@ -76,6 +81,7 @@
'',
@post_desc_null,
@peritem_null,
+ @currency_null,
'',
'',
'',
@@ -88,6 +94,7 @@
'',
@post_desc_null,
@peritem_null,
+ @currency_null,
'',
'',
'',
@@ -196,6 +203,23 @@ my @total_desc = ( $money_char.'%.2f total' ); # sprintf strings
my @peritem = ( 'setup', 'recur' );
my @peritem_desc = ( 'Setup charge', 'Recurring charge' );
+my @currency_desc = ();
+my @currency_sub = ();
+my @currency = ();
+if ( $conf->config('currencies') ) {
+ @currency_desc = ( 'Setup billed', 'Recurring billed' );
+ @currency_sub = (
+ map {
+ my $what = $_;
+ sub { my $currency = $_[0]->get($what.'_billed_currency');
+ $currency. ' '. currency_symbol($currency, SYM_HTML).
+ $_[0]->get($what.'_billed_amount');
+ };
+ } qw( setup recur )
+ );
+ @currency = ( 'setup_billed_amount', 'recur_billed_amount' ); #for sorting
+}
+
my @pkgnum_header = ();
my @pkgnum = ();
my @pkgnum_null;
@@ -672,6 +696,10 @@ my @peritem_sub = map {
my @peritem_null = map { '' } @peritem; # placeholders
my $peritem_align = 'r' x scalar(@peritem);
+@currency_desc = map {emt($_)} @currency_desc;
+my @currency_null = map { '' } @currency; # placeholders
+my $currency_align = 'r' x scalar(@currency);
+
my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html
index b863a734b..e286305f4 100644
--- a/httemplate/view/cust_main/billing.html
+++ b/httemplate/view/cust_main/billing.html
@@ -304,6 +304,13 @@
</TR>
% }
+% if ( $cust_main->currency ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Invoicing currency') |h %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->currency. ': '. code2currency($cust_main->currency) %></TD>
+ </TR>
+% }
+
% if ( $cust_main->locale ) {
% my %locale_info = FS::Locales->locale_info($cust_main->locale);
<TR>