File Coverage

blib/lib/Business/Price.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Business::Price;
2             our $VERSION = '1.000_1';
3              
4             # ABSTRACT: Handles prices with tax and discount
5              
6 1     1   89868 use Moose;
  0            
  0            
7             use MooseX::Types::Business::Price q(:all);
8              
9             has discount => ( is => 'rw', isa => Discount, default => 0 );
10              
11             has value => ( is => 'rw', isa => 'Num', required => 1 );
12              
13             has tax => ( is => 'rw', isa => Tax, default => 0 );
14              
15             has incl => ( is => 'rw', isa => 'Bool', default => 0 );
16              
17             use overload
18             '""' => sub { shift->value },
19             '=' => sub { shift->clone },
20             '+' => sub {
21             my $self = shift;
22             return $self->clone( value => $self->value + $_[0] );
23             },
24             '-' => sub {
25             my $self = shift;
26             return $self->clone( value => $self->value - $_[0] );
27             },
28             '*' => sub {
29             my $self = shift;
30             return $self->clone( value => $self->value * $_[0] );
31             },
32             '/' => sub {
33             my $self = shift;
34             return $self->clone( value => $self->value / $_[0] );
35             },
36             '%' => sub {
37             my $self = shift;
38             return $self->clone( value => $self->value % $_[0] );
39             },
40             '**' => sub {
41             my $self = shift;
42             return $self->clone( value => $self->value**$_[0] );
43             },
44             '<<' => sub {
45             my $self = shift;
46             return $self->clone( value => $self->value << $_[0] );
47             },
48             '>>' => sub {
49             my $self = shift;
50             return $self->clone( value => $self->value >> $_[0] );
51             },
52             'x' => sub {
53             my $self = shift;
54             return $self->clone( value => $self->value x $_[0] );
55             },
56             '<=>' => sub {
57             return shift->value <=> $_[0];
58             },
59             'cmp' => sub {
60             return shift->value cmp $_[0];
61             };
62              
63             sub discounted {
64             my ( $self, @args ) = @_;
65             my $discount = $self->discount;
66             if ( !ref $self->discount ) {
67             return $self->clone( value => $self->value * ( 1 - $self->discount ) );
68             }
69             elsif ( ref $self->discount eq 'CODE' ) {
70             return $self->discount->( $self, @args );
71             }
72             }
73              
74             sub full {
75             my $self = shift;
76             unless ( $self->incl ) {
77             return $self->clone(
78             value => $self->value * ( 1 + $self->tax ),
79             incl => 1
80             );
81             }
82             return $self->clone;
83             }
84              
85             sub net {
86             my $self = shift;
87             if ( $self->incl ) {
88             return $self->clone(
89             value => $self->value / ( 1 + $self->tax ),
90             incl => 0
91             );
92             }
93             return $self->clone;
94             }
95              
96             sub clone {
97             my $self = shift;
98             return bless( { %$self, @_ }, ref $self );
99             }
100              
101             __PACKAGE__->meta->make_immutable;
102              
103              
104             =pod
105              
106             =head1 NAME
107              
108             Business::Price - Handles prices with tax and discount
109              
110             =head1 VERSION
111              
112             version 1.000_1
113              
114             =head1 SYNOPSIS
115              
116             use Business::Price;
117             my $price = Business::Price->new( value => 10, tax => 0.1, discount => 0.5 );
118             is( $price->full, 11 );
119             is( $price->full->discounted, 5.5 );
120              
121             =head1 ATTRIBUTES
122              
123             =head2 value ( Num )
124              
125             Required
126              
127             The object stringifies to this attribute.
128              
129             =head2 discount ( Num | ArrayRef | HashRef | CodeRef )
130              
131             Default: 0
132              
133             =head2 tax ( Num )
134              
135             Default: 0
136              
137             Defines the tax rate. Valid values are from 0 to 1 (excluded).
138              
139             =head2 incl ( Bool )
140              
141             Default: 0
142              
143             Specifies whether L<value|/value ( Num )> includes tax or not.
144              
145             =head1 METHODS
146              
147             Methods will always return a new L<Business::Price> object.
148             It stringifies to L<value|/value ( Num )> and you can do math on those object
149             since it uses L<overload>.
150              
151             my $price = Business::Price->new( value => 10 );
152             ok( $price->isa('Business::Price') );
153             is( $price, 10 );
154             is( $price / 2, 5 );
155              
156             =head2 net
157              
158             Returns an object with the net price applied (i.e. without tax).
159              
160             my $price = Business::Price->new( value => 1.1, tax => 0.1, incl => 1);
161             is( $price->net, 1 );
162              
163             =head2 full
164              
165             Returns an object with the full price applied (i.e. tax applied).
166              
167             my $price = Business::Price->new( value => 1, tax => 0.1 );
168             is( $price->full, 1.1 );
169              
170             =head2 discounted ( Any )
171              
172             Discount the price by the format defined in L<discount|/discount ( Num | ArrayRef | HashRef | CodeRef )>.
173              
174             my $price = Business::Price->new( value => 1, discount => 0.5 );
175             is( $price->discounted, 0.5 );
176             is( $price->discounted->discounted, 0.25 );
177              
178              
179             my $price = Business::Price->new(
180             value => 1,
181             discount => sub {
182             my ( $self, $is_reseller ) = @_;
183             return $is_reseller ? $self / 2 : $self;
184             }
185             );
186             is( $price->discounted(0), 1 );
187             is( $price->discounted(1), 0.5 );
188              
189             =head2 clone ( Hash )
190              
191             Clone the object. Any parameters overwrite the object's L<attributes|/ATTRIBUTES>.
192              
193             my $price_excl = Business::Price->new( value => 1, incl => 0 );
194             my $price_incl = $price->clone( incl => 1 );
195              
196             =head1 SEE ALSO
197              
198             L<Business::Tax::Canada>, L<Business::Tax::VAT>, L<Moose>
199              
200             =head1 AUTHOR
201              
202             Moritz Onken <onken@netcubed.de>
203              
204             =head1 COPYRIGHT AND LICENSE
205              
206             This software is Copyright (c) 2010 by Moritz Onken.
207              
208             This is free software, licensed under:
209              
210             The (three-clause) BSD License
211              
212             =cut
213              
214              
215             __END__
216