Refactor cart to use "entries" instead of "items"

This commit is contained in:
Juerd Waalboer 2019-11-04 21:53:07 +01:00
parent ed03f09414
commit 5a7a7184dd
3 changed files with 95 additions and 85 deletions

View file

@ -3,6 +3,7 @@ use strict;
use Carp (); use Carp ();
use List::Util (); use List::Util ();
use RevBank::Global; use RevBank::Global;
use RevBank::Cart::Entry;
# Some code is written with the assumption that the cart will only grow or # Some code is written with the assumption that the cart will only grow or
# be emptied. Changing existing stuff or removing items is probably not a # be emptied. Changing existing stuff or removing items is probably not a
@ -10,115 +11,124 @@ use RevBank::Global;
sub new { sub new {
my ($class) = @_; my ($class) = @_;
return bless { items => {} }, $class; return bless { entries => [] }, $class;
}
sub _call_old_hooks {
my ($self, $hook, $entry) = @_;
my $data = $entry->{attributes};
for ($entry, $entry->contras) {
my $item = {
%$data,
amount => $_->{amount},
description => $_->{description},
};
RevBank::Plugins::call_hooks($hook, $self, $_->{user}, $item);
}
}
sub add_entry {
my ($self, $entry) = @_;
$self->_call_old_hooks("add", $entry);
RevBank::Plugins::call_hooks("add_entry", $self, $entry);
push @{ $self->{entries} }, $entry;
$self->{changed}++;
$self->_call_old_hooks("added", $entry);
RevBank::Plugins::call_hooks("added_entry", $self, $entry);
return $entry;
} }
sub add { sub add {
if (defined $_[3] and not ref $_[3]) {
my ($self, $user, $amount, $description, $data) = @_; my ($self, $user, $amount, $description, $data) = @_;
# Note: 'repeat' plugin is currently dependent on this specific Carp::carp("Plugin uses deprecated old-style call to \$cart->add");
# implementation!
$data ||= {}; $data->{COMPATIBILITY} = 1;
my $item = {
%$data, # Internal stuff, not logged or printed. my $entry = RevBank::Cart::Entry->new(
amount => $amount, defined $user ? 0 : $amount,
description => $description, $description,
}; $data
RevBank::Plugins::call_hooks("add", $self, $user, $item); );
push @{ $self->{items}{ $user || '$you' } }, $item; $entry->add_contra($user, $amount, $description) if defined $user;
$self->{changed}++; $entry->{FORCE} = 1;
RevBank::Plugins::call_hooks("added", $self, $user, $item);
return $self->add_entry($entry);
}
if (@_ == 2) {
my ($self, $entry) = @_;
return $self->add_entry($entry);
}
my ($self, $amount, $description, $data) = @_;
return $self->add_entry(RevBank::Cart::Entry->new($amount, $description, $data));
} }
sub delete { sub delete {
my ($self, $user, $index) = @_; Carp::croak("\$cart->delete(\$user, \$index) is no longer supported") if @_ > 2;
splice @{ $self->{items}{ $user } }, $index, 1, ();
my ($self, $entry) = @_;
my $entries = $self->{entries};
my $oldnum = @$entries;
@$entries = grep $_ != $entry, @$entries;
$self->{changed}++; $self->{changed}++;
return $oldnum - @$entries;
} }
sub empty { sub empty {
my ($self) = @_; my ($self) = @_;
%$self = (items => {});
$self->{entries} = [];
$self->{changed}++; $self->{changed}++;
} }
sub _dump_item {
my ($prefix, $user, $amount, $description) = @_;
return sprintf(
"%s%-12s %4s EUR %5.2f %s",
$prefix,
$user,
($amount > 0 ? 'GAIN' : $amount < 0 ? 'LOSE' : ''),
abs($amount),
$description
);
}
sub as_strings {
my ($self, $prefix) = @_;
$prefix ||= ' ';
my @s;
my $items = $self->{items};
for my $user (sort keys %$items) {
my @items = @{ $items->{$user} };
my $sum = List::Util::sum(map $_->{amount}, @items);
push @s, _dump_item($prefix, $user, $_->{amount}, "# $_->{description}")
for @items;
push @s, _dump_item($prefix, $user, $sum, "TOTAL")
if @items > 1;
}
return @s;
}
sub display { sub display {
my ($self, $prefix) = @_; my ($self, $prefix) = @_;
say $_ for $self->as_strings($prefix); $prefix //= "";
say "$prefix$_" for map $_->as_printable, @{ $self->{entries} };
}
sub as_strings {
my ($self) = @_;
Carp::carp("Plugin uses deprecated \$cart->as_strings");
return map $_->as_loggable, @{ $self->{entries} };
} }
sub size { sub size {
my ($self) = @_; my ($self) = @_;
my $items = $self->{items}; return scalar @{ $self->{entries} };
return List::Util::sum(map scalar @{ $items->{$_} }, keys %$items) || 0;
}
sub _set_user {
my ($self, $user) = @_;
my $items = $self->{items};
exists $items->{'$you'}
or Carp::croak("Error: no cart items for shell user");
$items->{$user} ||= [];
push @{ $items->{$user} }, @{ delete $items->{'$you'} };
for (values %$items) {
$_->{description} =~ s/\$you\b/$user/g for @$_;
}
} }
sub checkout { sub checkout {
my ($self, $user) = @_; my ($self, $user) = @_;
$self->_set_user($user) if $user; my $entries = $self->{entries};
my $items = $self->{items};
exists $items->{'$you'} and die "Incomplete transaction; user not set."; my %deltas;
for my $entry (@$entries) {
$entry->user($user);
$deltas{$_->{user}} += $_->{amount} for $entry, $entry->contras;
}
my $transaction_id = time() - 1300000000; my $transaction_id = time() - 1300000000;
RevBank::Plugins::call_hooks("checkout", $self, $user, $transaction_id); RevBank::Plugins::call_hooks("checkout", $self, $user, $transaction_id);
for my $account (keys %$items) { for my $account (keys %deltas) {
my $sum = List::Util::sum(map $_->{amount}, @{ $items->{$account} }); RevBank::Users::update($account, $deltas{$account}, $transaction_id);
RevBank::Users::update($account, $sum, $transaction_id);
} }
RevBank::Plugins::call_hooks("checkout_done", $self, $user,$transaction_id); RevBank::Plugins::call_hooks("checkout_done", $self, $user, $transaction_id);
$self->empty; $self->empty;
@ -127,14 +137,15 @@ sub checkout {
sub select_items { sub select_items {
my ($self, $key) = @_; my ($self, $key) = @_;
my $items = $self->{items}; Carp::carp("Plugin uses deprecated \$cart->select_items");
my @matches; my @matches;
for my $user (keys %$items) { for my $entry (@{ $self->{entries} }) {
for my $item (@{ $items->{$user} }) { my %attributes = %{ $entry->{attributes} };
push @matches, { user => $user, %$item } for my $item ($entry, $entry->contras) {
push @matches, { %attributes, %$item }
if @_ == 1 # No key or match given: match everything if @_ == 1 # No key or match given: match everything
or @_ == 2 and exists $item->{ $key } # Just a key or @_ == 2 and $entry->has_attribute($key) # Just a key
} }
} }
@ -142,8 +153,7 @@ sub select_items {
} }
sub is_multi_user { sub is_multi_user {
my ($self) = @_; Carp::carp("\$cart->is_multi_user is no longer supported, ignoring");
return keys(%{ $self->{items} }) > 1;
} }
sub changed { sub changed {

View file

@ -156,13 +156,13 @@ sub sanity_check {
# not required. However, in a transaction with contras, one should at least # not required. However, in a transaction with contras, one should at least
# not try to issue money that does not exist. # not try to issue money that does not exist.
return 1 if $self->{force}; return 1 if $self->{FORCE};
my @contras = $self->contras or return 1; my @contras = $self->contras or return 1;
my $amount = List::Util::sum(map $_->{amount}, $self, @contras); my $amount = List::Util::sum(map $_->{amount}, $self, @contras);
if ($amount >= 0.005) { # meh, floats if ($amount >= 0.005) { # meh, floats
$self->{force} = 1; $self->{FORCE} = 1;
croak join("\n", croak join("\n",
"BUG! (probably in $self->{caller})", "BUG! (probably in $self->{caller})",
"This adds up to creating money that does not exist:", "This adds up to creating money that does not exist:",

View file

@ -24,7 +24,7 @@ sub hook_plugin_fail {
sub hook_cart_changed { sub hook_cart_changed {
my ($class, $cart) = @_; my ($class, $cart) = @_;
$cart->size or return; $cart->size or return;
$cart->display(" "); $cart->display;
say "Enter username to pay/finish or 'abort' to abort.\n"; say "Enter username to pay/finish or 'abort' to abort.\n";
} }