From: Sven Schöling Date: Fri, 14 Sep 2012 10:10:21 +0000 (+0200) Subject: Form::format_amount - suabere trennung zwischen String und Numerischen Kontexten X-Git-Tag: release-3.0.0beta1~232^2~7 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=10478553c500772a988036a69947a7e6d19d404e;p=kivitendo-erp.git Form::format_amount - suabere trennung zwischen String und Numerischen Kontexten behebt #1982 (unter anderem) --- diff --git a/SL/Form.pm b/SL/Form.pm index 55ee46d4c..ff7d0efa0 100644 --- a/SL/Form.pm +++ b/SL/Form.pm @@ -866,41 +866,30 @@ sub format_amount { $main::lxdebug->enter_sub(2); my ($self, $myconfig, $amount, $places, $dash) = @_; - $dash ||= ''; + $amount ||= 0; + $dash ||= ''; + my $neg = $amount < 0; + my $force_places = defined $places && $places >= 0; - if ($amount eq "") { - $amount = 0; - } - - $amount *= 1; - - # Hey watch out! The amount can be an exponential term like 1.13686837721616e-13 + $amount = $self->round_amount($amount, abs $places) if $force_places; + $amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa - my $neg = ($amount =~ s/^-//); - my $exp = ($amount =~ m/[e]/) ? 1 : 0; + # before the sprintf amount was a number, afterwards it's a string. because of the dynamic nature of perl + # this is easy to confuse, so keep in mind: before this comment no s///, m//, concat or other strong ops on + # $amount. after this comment no +,-,*,/,abs. it will only introduce subtle bugs. - if (defined($places) && ($places ne '')) { - if (not $exp) { - if ($places < 0) { - $amount *= 1; - $places *= -1; - - if ($amount =~ /\.(\d+)/) { - my $actual_places = length $1; - $places = $actual_places if $actual_places > $places; - } - } - } - $amount = $self->round_amount($amount, $places); - } + $amount =~ s/0*$//; # cull trailing 0s my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars - my @p = split(/\./, $amount); # split amount at decimal point - - $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters + my @p = split(/\./, $amount); # split amount at decimal point + $p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters $amount = $p[0]; - $amount .= $d[0].($p[1]||'').(0 x ($places - length ($p[1]||''))) if ($places || $p[1] ne ''); + if ($places || $p[1]) { + $amount .= $d[0] + . ( $p[1] || '' ) + . (0 x (abs($places || 0) - length ($p[1]||''))); # pad the fraction + } $amount = do { ($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) : @@ -908,7 +897,6 @@ sub format_amount { ($neg ? "-$amount" : "$amount" ) ; }; - $main::lxdebug->leave_sub(2); return $amount; } diff --git a/t/form/format_amount.t b/t/form/format_amount.t index a88238ee8..58a7c17c8 100644 --- a/t/form/format_amount.t +++ b/t/form/format_amount.t @@ -25,6 +25,33 @@ is($::form->format_amount($config, 1000.1234, 2), '1,000.12', 'format 1000.1234 is($::form->format_amount($config, 1000000000.1234, 2), '1,000,000,000.12', 'format 1000000000.1234 (numberformat: 1,000.00)'); is($::form->format_amount($config, -1000000000.1234, 2), '-1,000,000,000.12', 'format -1000000000.1234 (numberformat: 1,000.00)'); +# negative places + +is($::form->format_amount($config, 1.00045, -2), '1.00045', 'negative places'); +is($::form->format_amount($config, 1.00045, -5), '1.00045', 'negative places 2'); +is($::form->format_amount($config, 1, -2), '1.00', 'negative places 3'); + +# bugs amd edge cases + +is($::form->format_amount({ numberformat => '1.000,00' }, 0.00005), '0,00005', 'messing with small numbers and no precision'); +is($::form->format_amount({ numberformat => '1.000,00' }, undef), '0', 'undef'); +is($::form->format_amount({ numberformat => '1.000,00' }, ''), '0', 'empty string'); +is($::form->format_amount({ numberformat => '1.000,00' }, undef, 2), '0,00', 'undef with precision'); +is($::form->format_amount({ numberformat => '1.000,00' }, '', 2), '0,00', 'empty string with prcesion'); + +is($::form->format_amount($config, 0.545, 0), '1', 'rounding up with precision 0'); +is($::form->format_amount($config, -0.545, 0), '-1', 'neg rounding up with precision 0'); + +is($::form->format_amount($config, 1.00), '1', 'autotrim to 0 places'); + + +# dash stuff + +$config->{numberformat} = '1.000,00'; + +is($::form->format_amount($config, -350, 2, '-'), '(350,00)', 'dash -'); + + done_testing; 1;