Basket-Calc
view release on metacpan or search on metacpan
lib/Basket/Calc.pm view on Meta::CPAN
package Basket::Calc;
use 5.010001;
use Mouse;
use experimental 'smartmatch';
# ABSTRACT: Basket/Cart calculation library with support for currency conversion, discounts and tax
our $VERSION = '0.5'; # VERSION
use Scalar::Util qw(looks_like_number);
use Finance::Currency::Convert::Yahoo;
use Carp;
has 'debug' => (
is => 'rw',
isa => 'Bool',
trigger => \&_set_debug,
lazy => 1,
default => sub { 0 },
);
has 'items' => (
is => 'rw',
isa => 'ArrayRef',
clearer => 'empty_items',
);
has 'discount' => (
is => 'rw',
isa => 'HashRef',
clearer => 'no_discount',
);
has 'currency' => (
is => 'rw',
isa => 'Str',
required => 1,
);
has 'tax' => (
is => 'rw',
isa => 'Num',
lazy => 1,
default => sub { 0 },
);
sub add_item {
my ($self, $item) = @_;
# make sure the input is sane
unless (ref $item eq 'HASH') {
carp "parameter has to be a HASHREF";
return;
}
foreach my $key ('price') {
unless (exists $item->{$key} and $item->{$key}) {
carp "$key missing";
return;
}
}
unless (looks_like_number($item->{price})) {
carp "'price' is not a number";
return;
}
# calculate amount from quantity and price
if (exists $item->{quantity}) {
if (!looks_like_number($item->{quantity}) || ($item->{quantity} < 0)) {
carp "'quantity' is not a number or smaller than 0";
return;
}
$item->{amount} = $item->{price} * $item->{quantity};
}
else {
$item->{amount} = $item->{price};
$item->{quantity} = 1;
}
$item->{currency} = $self->currency
unless (exists $item->{currency} and $item->{currency});
# convert currency if needed
if ($item->{currency} ne $self->currency) {
my $amount =
Finance::Currency::Convert::Yahoo::convert($item->{amount},
$item->{currency}, $self->currency);
unless ($amount) {
carp "could not get "
. $item->{amount} . " "
. $item->{currency}
. " converted to "
. $self->currency;
return;
}
$item->{orig_amount} = $item->{amount};
$item->{orig_currency} = $item->{currency};
$item->{amount} = $amount;
$item->{currency} = $self->currency;
}
print __PACKAGE__ . ' added item: ' . join(' ', %$item) . $/
if $self->debug;
$self->items([ @{ $self->items || [] }, $item ]);
return $item;
}
sub add_discount {
my ($self, $discount) = @_;
# make sure the input is sane
unless (ref $discount eq 'HASH') {
carp "parameter has to be a HASHREF";
return;
}
foreach my $key ('type', 'value') {
unless (exists $discount->{$key} and $discount->{$key}) {
carp "'$key' missing";
return;
}
}
unless ($discount->{type} =~ m/^(percent|amount)$/x) {
carp "'type' has to be either percent, or amount";
return;
}
unless (looks_like_number($discount->{value})) {
carp "'value' is not a number";
return;
}
given ($discount->{type}) {
when ('percent') {
if ($discount->{value} <= 0 or $discount->{value} > 1) {
carp "'percent' has to be a decimal between 0 and 1";
return;
}
}
when ('amount') {
$discount->{currency} = $self->currency
unless exists $discount->{currency};
# convert currency if needed
if ($discount->{currency} ne $self->currency) {
my $amount = Finance::Currency::Convert::Yahoo::convert(
$discount->{value}, $discount->{currency}, $self->currency);
unless ($amount) {
carp "could not get "
. $discount->{value} . " "
. $discount->{currency}
. " converted to "
. $self->currency;
return;
}
$discount->{orig_value} = $discount->{value};
$discount->{orig_currency} = $discount->{currency};
$discount->{value} = $amount;
$discount->{currency} = $self->currency;
}
}
}
print __PACKAGE__ . ' added discount: ' . join(' ', %$discount) . $/
if $self->debug;
$self->discount($discount);
return $self->discount;
}
sub calculate {
my ($self) = @_;
unless ($self->items) {
carp "no items added";
return;
}
my $total = {
value => 0,
net => 0,
tax_amount => 0,
discount => 0,
( run in 5.676 seconds using v1.01-cache-2.11-cpan-a5162978ef8 )