revbank/lib/RevBank/Cart.pm
2023-01-19 05:24:50 +01:00

137 lines
3.5 KiB
Perl

package RevBank::Cart;
use v5.28;
use warnings;
use feature qw(signatures);
no warnings qw(experimental::signatures);
use Carp ();
use List::Util ();
use RevBank::Global;
use RevBank::Users;
use RevBank::FileIO;
use RevBank::Cart::Entry;
sub new($class) {
return bless { entries => [] }, $class;
}
sub add_entry($self, $entry) {
RevBank::Plugins::call_hooks("add_entry", $self, $entry);
push @{ $self->{entries} }, $entry;
$self->{changed}++;
RevBank::Plugins::call_hooks("added_entry", $self, $entry);
return $entry;
}
sub add($self, $amount, $description, $data = {}) {
Carp::croak "Non-hash data argument; possibly a deprecated call style"
if not ref $data;
# Old pre-v3 call styles:
# ->add(undef, ...) => just remove the "undef,"
# ->add($user, ...) => use $cart->add(...)->add_contra($user, ...)
# ->add($entry) => use $cart->add_entry($entry)
return $self->add_entry(RevBank::Cart::Entry->new($amount, $description, $data));
}
sub delete($self, $entry) {
my $entries = $self->{entries};
my $oldnum = @$entries;
@$entries = grep $_ != $entry, @$entries;
$self->{changed}++;
return $oldnum - @$entries;
}
sub empty($self) {
$self->{entries} = [];
$self->{changed}++;
}
sub display($self, $prefix = "") {
say "$prefix$_" for map $_->as_printable, @{ $self->{entries} };
}
sub size($self) {
return scalar @{ $self->{entries} };
}
sub checkout($self, $user) {
if ($self->entries('refuse_checkout')) {
warn "Refusing to finalize deficient transaction.\n";
$self->display;
return;
}
$user = RevBank::Users::assert_user($user);
my $entries = $self->{entries};
for my $entry (@$entries) {
$entry->sanity_check;
$entry->user($user);
}
RevBank::FileIO::with_lock {
my $transaction_id = time() - 1300000000;
RevBank::Plugins::call_hooks("checkout_prepare", $self, $user, $transaction_id);
for my $entry (@$entries) {
$entry->sanity_check;
$entry->user($user) if not $entry->user;
}
RevBank::Plugins::call_hooks("checkout", $self, $user, $transaction_id);
my %deltas = ($user => RevBank::Amount->new(0));
for my $entry (@$entries) {
$deltas{$_->{user}} += $_->{amount} * $entry->quantity
for $entry, $entry->contras;
}
for my $account (reverse sort keys %deltas) {
# The reverse sort is a lazy way to make the "-" accounts come last,
# which looks nicer with the "cash" plugin.
RevBank::Users::update($account, $deltas{$account}, $transaction_id)
if $deltas{$account} != 0;
}
RevBank::Plugins::call_hooks("checkout_done", $self, $user, $transaction_id);
sleep 1; # look busy (and ensure new id for next transaction :))
};
$self->empty;
return 1;
}
sub entries($self, $attribute = undef) {
my @entries = @{ $self->{entries} };
return grep $_->has_attribute($attribute), @entries if defined $attribute;
return @entries;
}
sub changed($self, $keep = 0) {
my $changed = 0;
for my $entry ($self->entries('changed')) {
$entry->attribute('changed', undef) unless $keep;
$changed = 1;
}
$changed = 1 if $self->{changed};
delete $self->{changed} unless $keep;
return $changed;
}
sub sum($self) {
return List::Util::sum(map $_->{amount} * $_->quantity, @{ $self->{entries} });
}
1;