File Coverage

blib/lib/Data/Money.pm
Criterion Covered Total %
statement 150 165 90.9
branch 55 66 83.3
condition 11 24 45.8
subroutine 36 40 90.0
pod 14 18 77.7
total 266 313 84.9


line stmt bran cond sub pod time code
1             package Data::Money;
2              
3             $Data::Money::VERSION = '0.18';
4             $Data::Money::AUTHORITY = 'cpan:GPHAT';
5              
6             =head1 NAME
7              
8             Data::Money - Money/currency with formatting and overloading.
9              
10             =head1 VERSION
11              
12             Version 0.18
13              
14             =cut
15              
16 8     8   517506 use 5.006;
  8         77  
17 8     8   4800 use Moo;
  8         95202  
  8         42  
18 8     8   15579 use namespace::clean;
  8         97374  
  8         53  
19              
20 8     8   7172 use Data::Dumper;
  8         53781  
  8         547  
21 8     8   8480 use Math::BigFloat;
  8         459828  
  8         52  
22 8     8   196995 use Data::Money::BaseException::MismatchCurrencyType;
  8         37  
  8         338  
23 8     8   3836 use Data::Money::BaseException::ExcessivePrecision;
  8         23  
  8         274  
24 8     8   3603 use Data::Money::BaseException::InvalidCurrencyCode;
  8         21  
  8         269  
25 8     8   3532 use Data::Money::BaseException::InvalidCurrencyFormat;
  8         25  
  8         330  
26 8     8   4727 use Locale::Currency::Format;
  8         39060  
  8         851  
27 8     8   3716 use Locale::Currency qw(code2currency);
  8         135199  
  8         2151  
28              
29             use overload
30             '+' => \&add,
31             '-' => \&subtract,
32             '*' => \&multiply,
33             '/' => \÷,
34             '%' => \&modulo,
35             '+=' => \&add_in_place,
36             '-=' => \&subtract_in_place,
37             '*=' => \&multiply_in_place,
38             '/=' => \÷_in_place,
39 0     0   0 '0+' => sub { $_[0]->value->numify; },
40 0     0   0 '""' => sub { shift->stringify },
41 51     51   617 'bool' => sub { shift->as_int; },
42             '<=>' => \&three_way_compare,
43             'cmp' => \&three_way_compare,
44 2     2   704 'abs' => sub { shift->absolute },
45 0     0   0 '=' => sub { shift->clone },
46 8         207 'neg' => \&negate,
47 8     8   89 fallback => 1;
  8         17  
48              
49             my $Amount = sub {
50             my ($arg) = @_;
51              
52             return Math::BigFloat->new(0) unless defined $arg;
53              
54             return Math::BigFloat->new($arg->value)
55             if (ref($arg) eq 'Data::Money');
56              
57             $arg =~ tr/-()0-9.//cd;
58             if ($arg) {
59             Math::BigFloat->new($arg);
60             } else {
61             Math::BigFloat->new(0);
62             }
63             };
64              
65             my $CurrencyCode = sub {
66             my ($arg) = @_;
67              
68             Data::Money::BaseException::InvalidCurrencyCode->throw
69             unless (defined $arg
70             || ($arg =~ /^[A-Z]{3}$/mxs && defined code2currency($arg)));
71             };
72              
73             my $Format = sub {
74             my ($arg) = @_;
75              
76             my $format = {
77             'FMT_COMMON' => 1,
78             'FMT_HTML' => 1,
79             'FMT_NAME' => 1,
80             'FMT_STANDARD' => 1,
81             'FMT_SYMBOL' => 1
82             };
83              
84             Data::Money::BaseException::InvalidCurrencyFormat->throw
85             unless (defined $arg || exists $format->{uc($arg)});
86             };
87              
88             has code => (is => 'rw', isa => $CurrencyCode, default => sub { 'USD' });
89             has format => (is => 'rw', isa => $Format, default => sub { 'FMT_COMMON' });
90             has value => (is => 'rw', isa => $Amount, default => sub { Math::BigFloat->new(0) }, coerce => $Amount);
91              
92             =head1 DESCRIPTION
93              
94             The C module provides basic currency formatting and number handling
95             via L:
96              
97             my $currency = Data::Money->new(value => 1.23);
98              
99             Each C object will stringify to the original value except in string
100             context, where it stringifies to the format specified in C.
101              
102             =head1 MOTIVATION
103              
104             Data::Money was created to make it easy to use different currencies (leveraging
105             existing work in C and L), to allow math operations
106             with proper rounding (via L) and formatting via L.
107              
108             =head1 SYNOPSIS
109              
110             use strict; use warnings;
111             use Data::Money;
112              
113             my $price = Data::Money->new(value => 1.2, code => 'USD');
114             print $price; # $1.20
115             print $price->code; # USD
116             print $price->format; # FMT_COMMON
117             print $price->as_string; # $1.20
118              
119             # Overloading, returns new instance
120             my $m2 = $price + 1;
121             my $m3 = $price - 1;
122             my $m4 = $price * 1;
123             my $m5 = $price / 1;
124             my $m6 = $price % 1;
125              
126             # Objects work too
127             my $m7 = $m2 + $m3;
128             my $m8 = $m2 - $m3;
129             my $m9 = $m2 * $m3;
130             my $m10 = $m2 / $m3;
131              
132             # Modifies in place
133             $price += 1;
134             $price -= 1;
135             $price *= 1;
136             $price /= 1;
137              
138             # Compares against numbers
139             print "Currency > 2 \n" if ($m2 > 2);
140             print "Currency < 3 \n" if ($m2 < 3);
141             print "Currency == 2.2 \n" if ($m2 == 2.2);
142              
143             # And strings
144             print "Currency gt \$2.00 \n" if ($m2 gt '$2.00');
145             print "Currency lt \$3.00 \n" if ($m2 lt '$3.00');
146             print "Currency eq \$2.20 \n" if ($m2 eq '$2.20');
147              
148             # and objects
149             print "Currency m2 > m3 \n" if ($m2 > $m3);
150             print "Currency m3 lt m2 \n" if ($m3 lt $m2);
151              
152             print $price->as_string('FMT_SYMBOL'); # $1.20
153              
154             =cut
155              
156             sub BUILD {
157 164     164 0 27305 my ($self) = @_;
158              
159 164         290 my $exp = 0;
160 164         2666 my $dec = $self->value->copy->bmod(1);
161 164 100       54576 $exp = $dec->exponent->babs if ($dec);
162 164         9197 my $prec = Math::BigInt->new($self->_decimal_precision);
163              
164 163 100       9840 Data::Money::BaseException::ExcessivePrecision->throw if ($exp > $prec);
165             }
166              
167             =head1 METHODS
168              
169             =head2 name()
170              
171             Returns C object currency name.
172              
173             =head2 code($currency_code)
174              
175             Gets/sets the three letter currency code for the current currency object.Defaults
176             to USD.
177              
178             =head2 value()
179              
180             Returns the amount. Defaults to 0.
181              
182             =head2 format($string)
183              
184             Gets/sets the format to be used when C is called. See L
185             for the available formatting options. Defaults to C.
186              
187             =cut
188              
189             sub name {
190 0     0 1 0 my ($self) = @_;
191              
192 0         0 my $name = code2currency($self->code);
193             ## Fix for older Locale::Currency w/mispelled Candian
194 0         0 $name =~ s/Candian/Canadian/ms;
195              
196 0         0 return $name;
197             }
198              
199             =head2 clone(%params)
200              
201             Returns a clone (new instance) of this C object. You may optionally
202             specify some of the attributes to overwrite.
203              
204             $currency->clone({ value => 100 }); # Clones all fields but changes value to 100
205              
206             =cut
207              
208             sub clone {
209 97     97 1 8672 my ($self, %param) = @_;
210              
211 97 50 33     1964 $param{code} = $self->code unless (exists $param{code} && defined $param{code});
212 97 50 33     2142 $param{format} = $self->format unless (exists $param{format} && defined $param{format});
213 97         1956 return $self->new(\%param);
214             }
215              
216             =head2 as_float()
217              
218             Returns C object value without any formatting.
219              
220             =cut
221              
222             # Liberally jacked from Math::Currency
223             sub as_float {
224 69     69 1 2342 my ($self) = @_;
225              
226 69         1431 return $self->value->copy->bfround(0 - $self->_decimal_precision)->bstr;
227             }
228              
229             =head2 as_int()
230              
231             Returns the object's value "in pennies" (in the US at least). It strips the value
232             of formatting using C and of any decimals.
233              
234             =cut
235              
236             # Liberally jacked from Math::Currency
237             sub as_int {
238 51     51 1 95 my ($self) = @_;
239              
240 51         109 (my $str = $self->as_float) =~ s/\.//omsx;
241 51         8884 $str =~ s/^(\-?)0+/$1/omsx;
242 51 100       277 return $str eq '' ? '0' : $str;
243             }
244              
245             =head2 absolute()
246              
247             Returns a new C object with the value set to the absolute value of
248             the original.
249              
250             =cut
251              
252             sub absolute {
253 20     20 1 421 my ($self) = @_;
254              
255 20         384 return $self->clone(value => abs $self->value);
256             }
257              
258             =head2 negate()
259              
260             Performs the negation operation, returning a new C object with the
261             opposite value (1 to -1, -2 to 2, etc).
262              
263             =cut
264              
265             sub negate {
266 4     4 1 1952 my ($self) = @_;
267              
268 4 100       94 return $self->absolute if ($self->value < 0);
269              
270 2         478 my $val = 0 - $self->value;
271 2         531 return $self->clone(value => $val);
272             }
273              
274             =head2 add($num)
275              
276             Adds the specified amount to this C object and returns a new C
277             object. You can supply either a number or a C object. Note that this B
278             modify the existing object.
279              
280             =cut
281              
282             sub add {
283 6     6 1 1167 my $self = shift;
284 6   50     18 my $num = shift || 0;
285              
286 6 100       61 if (ref($num) eq ref($self)) {
287 3 100       66 Data::Money::BaseException::MismatchCurrencyType->throw
288             if ($self->code ne $num->code);
289              
290 1         29 return $self->clone(value => $self->value->copy->badd($num->value));
291             }
292              
293 3         80 return $self->clone(value => $self->value->copy->badd($self->clone(value => $num)->value))
294             }
295              
296             =head2 add_in_place($num)
297              
298             Adds the specified amount to this C object, modifying its value. You
299             can supply either a number or a C object. Note that this B
300             modify the existing object.
301              
302             =cut
303              
304             sub add_in_place {
305 3     3 1 906 my ($self, $num) = @_;
306              
307 3 100       13 if (ref($num) eq ref($self)) {
308 2 100       49 Data::Money::BaseException::MismatchCurrencyType->throw
309             if ($self->code ne $num->code);
310              
311 1         31 $self->value($self->value->copy->badd($num->value));
312             } else {
313 1         22 $self->value($self->value->copy->badd($self->clone(value => $num)->value));
314             }
315              
316 2         243 return $self;
317             }
318              
319             =head2 as_string()
320              
321             Returns C object as string.There is an alias C as well.
322              
323             =cut
324              
325             *as_string = \&stringify;
326             sub stringify {
327 16     16 0 1494 my $self = shift;
328 16   33     366 my $format = shift || $self->format;
329              
330             ## funky eval to get string versions of constants back into the values
331 16         1020 eval '$format = Locale::Currency::Format::' . $format;
332              
333 16         304 my $code = $self->code;
334 16 50 0     136 Data::Money::BaseException::InvalidCurrencyCode->throw(
335             {
336             error => 'Invalid currency code: ' . ($code || 'undef')
337             })
338             unless (_is_CurrencyCode($code));
339              
340 16         727 my $utf8 = _to_utf8(
341             Locale::Currency::Format::currency_format($code, $self->absolute->as_float, $format)
342             );
343              
344 16 50       414 if ($self->value < 0) {
345 0         0 return "-$utf8";
346             } else {
347 16         3513 return $utf8;
348             }
349             }
350              
351             =head2 substract($num)
352              
353             Subtracts the specified amount to this C object and returns a new
354             C object. You can supply either a number or a C object.
355             Note that this B modify the existing object.
356              
357             =cut
358              
359             sub subtract {
360 5     5 0 1014 my $self = shift;
361 5   50     19 my $num = shift || 0;
362              
363 5 100       25 if (ref($num) eq ref($self)) {
364 4 100       82 Data::Money::BaseException::MismatchCurrencyType->throw
365             if ($self->code ne $num->code);
366              
367 3         151 return $self->clone(value => $self->value->copy->bsub($num->value));
368             }
369              
370 1         25 return $self->clone(value => $self->value->copy->bsub($self->clone(value => $num)->value))
371             }
372              
373             =head2 substract_in_place($num)
374              
375             Subtracts the specified amount to this C object,modifying its value.
376             You can supply either a number or a C object. Note that this B
377             modify the existing object.
378              
379             =cut
380              
381             sub subtract_in_place {
382 3     3 0 407 my ($self, $num) = @_;
383              
384 3 100       10 if (ref($num) eq ref($self)) {
385 2 100       46 Data::Money::BaseException::MismatchCurrencyType->throw
386             if ($self->code ne $num->code);
387              
388 1         45 $self->value($self->value->copy->bsub($num->value));
389             } else {
390 1         22 $self->value($self->value->copy->bsub($self->clone(value => $num)->value));
391             }
392              
393 2         419 return $self;
394             }
395              
396             =head2 multiply($num)
397              
398             Multiplies the value of this C object and returns a new C
399             object. You dcan dsupply either a number or a C object. Note that this
400             B modify the existing object.
401              
402             =cut
403              
404             sub multiply {
405 7     7 1 492 my ($self, $num) = @_;
406              
407 7 100       27 if (ref($num) eq ref($self)) {
408 3 100       73 Data::Money::BaseException::MismatchCurrencyType->throw
409             if ($self->code ne $num->code);
410              
411 2         50 return $self->clone(value => $self->value->copy->bmul($num->value));
412             }
413              
414 4         92 return $self->clone(value => $self->value->copy->bmul($self->clone(value => $num)->value))
415             }
416              
417             =head2 multiply_in_place($num)
418              
419             Multiplies the value of this C object, modifying its value. You can
420             supply either a number or a C object. Note that this B modify
421             the existing object.
422              
423             =cut
424              
425             sub multiply_in_place {
426 3     3 1 317 my ($self, $num) = @_;
427              
428 3 50       13 if (ref($num) eq ref($self)) {
429 3 100       72 Data::Money::BaseException::MismatchCurrencyType->throw
430             if ($self->code ne $num->code);
431              
432 2         52 $self->value($self->value->copy->bmul($num->value));
433             } else {
434 0         0 $self->value($self->value->copy->bmul($self->clone(value => $num)->value));
435             }
436              
437 2         425 return $self;
438             }
439              
440             =head2 divide($num)
441              
442             Divides the value of this C object and returns a new C
443             object. You can supply either a number or a C object. Note that this
444             B modify the existing object.
445              
446             =cut
447              
448             sub divide {
449 5     5 1 444 my ($self, $num) = @_;
450              
451 5 100       21 if (ref($num) eq ref($self)) {
452 3 100       73 Data::Money::BaseException::MismatchCurrencyType->throw
453             if ($self->code ne $num->code);
454              
455 2         51 my $val = $self->value->copy->bdiv($num->value);
456 2         1118 return $self->clone(value => $self->_round_up($val));
457             }
458              
459 2         45 my $val = $self->value->copy->bdiv($self->clone(value => $num)->value);
460 2         1469 return $self->clone(value => $self->_round_up($val));
461             }
462              
463             =head2 divide_in_place($num)
464              
465             Divides the value of this C object, modifying its value. You can
466             supply either a number or a C object. Note that this B modify
467             the existing object.
468              
469             =cut
470              
471             sub divide_in_place {
472 5     5 1 317 my ($self, $num) = @_;
473              
474 5         9 my $val;
475 5 100       19 if (ref($num) eq ref($self)) {
476 3 100       71 Data::Money::BaseException::MismatchCurrencyType->throw
477             if ($self->code ne $num->code);
478              
479 2         50 $val = $self->value->copy->bdiv($num->value);
480             } else {
481 2         107 $val = $self->value->copy->bdiv($self->clone(value => $num));
482             }
483              
484 4         2670 $self->value($self->_round_up($val));
485              
486 4         753 return $self;
487             }
488              
489             =head2 modulo($num)
490              
491             Performs the modulo operation on this C object, returning a new C
492             object with the value of the remainder.
493              
494             =cut
495              
496             sub modulo {
497 1     1 1 366 my ($self, $num) = @_;
498              
499 1 50       5 if (ref($num) eq ref($self)) {
500 1 50       24 Data::Money::BaseException::MismatchCurrencyType->throw
501             if ($self->code ne $num->code);
502              
503 0         0 my $val = $self->value->copy->bmod($num->value);
504 0         0 return $self->clone(value => $val);
505             }
506              
507 0         0 my $val = $self->value->copy->bmod($self->clone(value => $num)->value);
508 0         0 return $self->clone(value => $val);
509             }
510              
511             =head2 three_way_compare($num)
512              
513             Compares a C object to another C object, or anything it
514             is capable of coercing - numbers, numerical strings, or L objects.
515             Both numerical and string comparators work.
516              
517             =cut
518              
519             sub three_way_compare {
520 58     58 1 17727 my $self = shift;
521 58   100     153 my $num = shift || 0;
522              
523 58         86 my $other;
524 58 100       171 if (ref($num) eq ref($self)) {
525 15         21 $other = $num;
526             } else {
527             # we clone here to ensure that if we're comparing a number to
528             # an object, that the currency codes match (and we don't just
529             # get the default).
530 43         104 $other = $self->clone(value => $num);
531             }
532              
533 58 100       3870 Data::Money::BaseException::MismatchCurrencyType->throw(
534             {
535             error => 'Unable to compare different currency types.'
536             })
537             if ($self->code ne $other->code);
538              
539 54         1257 return $self->value->copy->bfround(0 - $self->_decimal_precision) <=> $other->value->copy->bfround(0 - $self->_decimal_precision);
540             }
541              
542             #
543             #
544             # PRIVATE METHODS
545              
546             sub _decimal_precision {
547 341     341   14280 my ($self, $code) = @_;
548              
549 341   33     6647 $code ||= $self->code;
550              
551 341         2365 my $format;
552             ## funky eval to get string versions of constants back into the values
553 341         5235 eval '$format = Locale::Currency::Format::' . $self->format;
554              
555 341 100 50     1519 Data::Money::BaseException::InvalidCurrencyCode->throw(
556             {
557             error => 'Invalid currency code: ' . ($code || 'undef')
558             })
559             unless (_is_CurrencyCode($code));
560              
561 340   100     15805 return Locale::Currency::Format::decimal_precision($code) || 0;
562             }
563              
564             sub _round_up {
565 8     8   18 my ($self, $val) = @_;
566              
567 8         169 my $prec = Locale::Currency::Format::decimal_precision($self->code);
568 8         154 return sprintf('%.0'.$prec.'f', _round($val, $prec*-1));
569             }
570              
571             sub _to_utf8 {
572 16     16   3168 my $value = shift;
573              
574 16 50       43 if ($] >= 5.008) {
575 16         86 require utf8;
576 16         48 utf8::upgrade($value);
577             };
578              
579 16         37 return $value;
580             };
581              
582             sub _is_CurrencyCode {
583 357     357   734 my ($code) = @_;
584              
585 357 50       784 return 0 unless defined $code;
586              
587 357         1021 return defined code2currency($code, 'alpha');
588             }
589              
590             # http://www.perlmonks.org/?node_id=24335
591             sub _round {
592 8     8   17 my ($number, $places) = @_;
593              
594 8 50       25 my $sign = ($number < 0) ? '-' : '';
595 8         1636 my $abs = abs($number);
596              
597 8 50       353 if ($places < 0) {
598 8         10 $places *= -1;
599 8         37 return $sign . substr($abs+("0." . "0" x $places . "5"), 0, $places+length(int($abs))+1);
600             } else {
601 0           my $p10 = 10**$places;
602 0           return $sign . int($abs/$p10 + 0.5)*$p10;
603             }
604             }
605              
606             =head1 OPERATOR OVERLOADING
607              
608             C overrides some operators. It is important to note which operators
609             change the object's value and which return new ones.All operators accept either a
610             C argument / a normal number via scalar and will die if the currency
611             types mismatch.
612              
613             C overloads the following operators:
614              
615             =over 4
616              
617             =item +
618              
619             Handled by the C method. Returns a new C object.
620              
621             =item -
622              
623             Handled by the C method. Returns a new C object.
624              
625             =item S< >*
626              
627             Handled by the C method. Returns a new C object.
628              
629             =item /
630              
631             Handled by the C method. Returns a new C object.
632              
633             =item +=
634              
635             Handled by the C method. Modifies the left-hand object's value.
636             Works with either a C argument or a normal number.
637              
638             =item -=
639              
640             Handled by the C method. Modifies the left-hand object's
641             value. Works with either a C argument or a normal number.
642              
643             =item *=
644              
645             Handled by the C method. Modifies the left-hand object's
646             value. Works with either a C argument or a normal number.
647              
648             =item /=
649              
650             Handled by the C method. Modifies the left-hand object's
651             value. Works with either a C argument or a normal number.
652              
653             =item <=>
654              
655             Performs a three way comparsion. Works with either a Data::Money argument or a
656             normal number.
657              
658             =back
659              
660             =head1 SEE ALSO
661              
662             =over 4
663              
664             =item L
665              
666             =item L
667              
668             =back
669              
670             =head1 ACKNOWLEDGEMENTS
671              
672             This module was originally based on L by Christopher H. Laco but I
673             I opted to fork and create a whole new module because my work was wildly different
674             from the original. I decided it was better to make a new module than to break back
675             compat and surprise users. Many thanks to him for the great module.
676              
677             Inspiration and ideas were also drawn from L and L.
678              
679             Major contributions (more overloaded operators, disallowing operations on mismatched
680             currences, absolute value, negation and unit tests) from Andrew Nelson C<< >>.
681              
682             =head1 AUTHOR
683              
684             Cory G Watson, C<< >>
685              
686             Currently maintained by Mohammad S Anwar (MANWAR) C<< >>
687              
688             =head1 REPOSITORY
689              
690             L
691              
692             =head1 LICENSE AND COPYRIGHT
693              
694             Copyright 2010 Cory Watson
695              
696             This program is free software; you can redistribute it and/or modify it under the
697             terms of either: the GNU General Public License as published by the Free Software
698             Foundation; or the Artistic License.
699              
700             See L for more information.
701              
702             =cut
703              
704             1; # End of Data::Money