Revision a967d2a4
Von Sven Schöling vor mehr als 4 Jahren hinzugefügt
SL/Form.pm | ||
---|---|---|
42 | 42 |
use Data::Dumper; |
43 | 43 |
|
44 | 44 |
use Carp; |
45 |
use Config; |
|
46 | 45 |
use CGI; |
47 | 46 |
use Cwd; |
48 | 47 |
use Encode; |
... | ... | |
88 | 87 |
use List::MoreUtils qw(all any apply); |
89 | 88 |
use SL::DB::Tax; |
90 | 89 |
use SL::Helper::File qw(:all); |
90 |
use SL::Helper::Number; |
|
91 | 91 |
use SL::Helper::CreatePDF qw(merge_pdfs); |
92 | 92 |
|
93 | 93 |
use strict; |
... | ... | |
699 | 699 |
return @columns; |
700 | 700 |
} |
701 | 701 |
# |
702 |
sub format_amount { |
|
703 |
$main::lxdebug->enter_sub(2); |
|
704 |
|
|
705 |
my ($self, $myconfig, $amount, $places, $dash) = @_; |
|
706 |
$amount ||= 0; |
|
707 |
$dash ||= ''; |
|
708 |
my $neg = $amount < 0; |
|
709 |
my $force_places = defined $places && $places >= 0; |
|
710 |
|
|
711 |
$amount = $self->round_amount($amount, abs $places) if $force_places; |
|
712 |
$neg = 0 if $amount == 0; # don't show negative zero |
|
713 |
$amount = sprintf "%.*f", ($force_places ? $places : 10), abs $amount; # 6 is default for %fa |
|
714 |
|
|
715 |
# before the sprintf amount was a number, afterwards it's a string. because of the dynamic nature of perl |
|
716 |
# this is easy to confuse, so keep in mind: before this comment no s///, m//, concat or other strong ops on |
|
717 |
# $amount. after this comment no +,-,*,/,abs. it will only introduce subtle bugs. |
|
718 |
|
|
719 |
$amount =~ s/0*$// unless defined $places && $places == 0; # cull trailing 0s |
|
720 |
|
|
721 |
my @d = map { s/\d//g; reverse split // } my $tmp = $myconfig->{numberformat}; # get delim chars |
|
722 |
my @p = split(/\./, $amount); # split amount at decimal point |
|
723 | 702 |
|
724 |
$p[0] =~ s/\B(?=(...)*$)/$d[1]/g if $d[1]; # add 1,000 delimiters |
|
725 |
$amount = $p[0]; |
|
726 |
if ($places || $p[1]) { |
|
727 |
$amount .= $d[0] |
|
728 |
. ( $p[1] || '' ) |
|
729 |
. (0 x max(abs($places || 0) - length ($p[1]||''), 0)); # pad the fraction |
|
730 |
} |
|
731 |
|
|
732 |
$amount = do { |
|
733 |
($dash =~ /-/) ? ($neg ? "($amount)" : "$amount" ) : |
|
734 |
($dash =~ /DRCR/) ? ($neg ? "$amount " . $main::locale->text('DR') : "$amount " . $main::locale->text('CR') ) : |
|
735 |
($neg ? "-$amount" : "$amount" ) ; |
|
736 |
}; |
|
737 |
|
|
738 |
$main::lxdebug->leave_sub(2); |
|
739 |
return $amount; |
|
740 |
} |
|
703 |
sub format_amount { shift; goto &SL::Helper::Number::_number; } |
|
741 | 704 |
|
742 | 705 |
sub format_amount_units { |
743 | 706 |
$main::lxdebug->enter_sub(); |
... | ... | |
824 | 787 |
|
825 | 788 |
# |
826 | 789 |
|
827 |
sub parse_amount { |
|
828 |
$main::lxdebug->enter_sub(2); |
|
829 |
|
|
830 |
my ($self, $myconfig, $amount) = @_; |
|
790 |
sub parse_amount { shift; goto &SL::Helper::Number::_parse_number; } |
|
831 | 791 |
|
832 |
if (!defined($amount) || ($amount eq '')) { |
|
833 |
$main::lxdebug->leave_sub(2); |
|
834 |
return 0; |
|
835 |
} |
|
836 |
|
|
837 |
if ( ($myconfig->{numberformat} eq '1.000,00') |
|
838 |
|| ($myconfig->{numberformat} eq '1000,00')) { |
|
839 |
$amount =~ s/\.//g; |
|
840 |
$amount =~ s/,/\./g; |
|
841 |
} |
|
842 |
|
|
843 |
if ($myconfig->{numberformat} eq "1'000.00") { |
|
844 |
$amount =~ s/\'//g; |
|
845 |
} |
|
846 |
|
|
847 |
$amount =~ s/,//g; |
|
848 |
|
|
849 |
$main::lxdebug->leave_sub(2); |
|
850 |
|
|
851 |
# Make sure no code wich is not a math expression ends up in eval(). |
|
852 |
return 0 unless $amount =~ /^ [\s \d \( \) \- \+ \* \/ \. ]* $/x; |
|
853 |
|
|
854 |
# Prevent numbers from being parsed as octals; |
|
855 |
$amount =~ s{ (?<! [\d.] ) 0+ (?= [1-9] ) }{}gx; |
|
856 |
|
|
857 |
return scalar(eval($amount)) * 1 ; |
|
858 |
} |
|
859 |
|
|
860 |
sub round_amount { |
|
861 |
my ($self, $amount, $places, $adjust) = @_; |
|
862 |
|
|
863 |
return 0 if !defined $amount; |
|
864 |
|
|
865 |
$places //= 0; |
|
866 |
|
|
867 |
if ($adjust) { |
|
868 |
my $precision = $::instance_conf->get_precision || 0.01; |
|
869 |
return $self->round_amount( $self->round_amount($amount / $precision, 0) * $precision, $places); |
|
870 |
} |
|
871 |
|
|
872 |
# We use Perl's knowledge of string representation for |
|
873 |
# rounding. First, convert the floating point number to a string |
|
874 |
# with a high number of places. Then split the string on the decimal |
|
875 |
# sign and use integer calculation for rounding the decimal places |
|
876 |
# part. If an overflow occurs then apply that overflow to the part |
|
877 |
# before the decimal sign as well using integer arithmetic again. |
|
878 |
|
|
879 |
my $int_amount = int(abs $amount); |
|
880 |
my $str_places = max(min(10, 16 - length("$int_amount") - $places), $places); |
|
881 |
my $amount_str = sprintf '%.*f', $places + $str_places, abs($amount); |
|
882 |
|
|
883 |
return $amount unless $amount_str =~ m{^(\d+)\.(\d+)$}; |
|
884 |
|
|
885 |
my ($pre, $post) = ($1, $2); |
|
886 |
my $decimals = '1' . substr($post, 0, $places); |
|
887 |
|
|
888 |
my $propagation_limit = $Config{i32size} == 4 ? 7 : 18; |
|
889 |
my $add_for_rounding = substr($post, $places, 1) >= 5 ? 1 : 0; |
|
890 |
|
|
891 |
if ($places > $propagation_limit) { |
|
892 |
$decimals = Math::BigInt->new($decimals)->badd($add_for_rounding); |
|
893 |
$pre = Math::BigInt->new($decimals)->badd(1) if substr($decimals, 0, 1) eq '2'; |
|
894 |
|
|
895 |
} else { |
|
896 |
$decimals += $add_for_rounding; |
|
897 |
$pre += 1 if substr($decimals, 0, 1) eq '2'; |
|
898 |
} |
|
899 |
|
|
900 |
$amount = ("${pre}." . substr($decimals, 1)) * ($amount <=> 0); |
|
901 |
|
|
902 |
return $amount; |
|
903 |
} |
|
792 |
sub round_amount { shift; goto &SL::Helper::Number::_round_number; } |
|
904 | 793 |
|
905 | 794 |
sub parse_template { |
906 | 795 |
$main::lxdebug->enter_sub(); |
Auch abrufbar als: Unified diff
SL::Helper::Number - format_amount aus SL::Form ausgelagert
- Neue Funktionen können direkt exportiert werden.
- Alte Funktionen sind in SL::Form für Kompatibilität auf die neuen
geproxied.
- Tracing debug statements (enter_sub, leave_sub) entfernt
- Zusätzlich noch eine neue Kategorie eingeführt: total, um Werte für
acc_trans immer auf 2 Stellen zu runden.