revbank/plugins/repeat
Juerd Waalboer 19691e07b8 Remove overzealous checks from repeat plugin
There are some cases where repetition can be used with multi user transaction,
specifically if the repetition is done before any multi user stuff is added...
2019-05-12 21:47:06 +02:00

114 lines
3.1 KiB
Perl
Executable file

#!perl
HELP "*<N>, x<N>, <N>x, <N>*" => "Repeat previous/next product N times";
my $err_stacked = "Stacked repetition is not supported.";
my $err_multi = "Repetition not supported in multi-user transactions.";
my $err_pfand = "Plugins 'pfand' and 'repeat' cannot be combined.";
my $limit = 24;
my $err_limit = "Repetition is limited at $limit items.";
sub _do_repeat {
my ($cart, $item, $num) = @_;
$item->{_repeated} = 1;
# Strongly dependent on the implementation of RevBank::Cart and the 'add'
# method. Bad idea, but meh.
$cart->add( @{ $item }{qw/user amount description/}, $item ) for 2..$num;
}
sub command {
my ($self, $cart, $command) = @_;
my @items = $cart->select_items;
my $last = $items[-1];
return ABORT, $err_pfand if grep $_->{is_pfand}, @items;
my ($pre, $post) = $command =~ /^(\d+)?[x*](\d+)?$/
or return NEXT;
return NEXT if $pre and $post; # 123x123 -> invalid syntax
if ($post) {
return REJECT, $err_multi if $cart->is_multi_user;
return REJECT, $err_limit if $post > $limit;
return ABORT, "Can't repeat an empty transaction." if not $cart->size;
return REJECT, $err_stacked if $last->{_repeated};
_do_repeat($cart, $last, $post);
return ACCEPT;
}
my $item_replaced;
if (not $pre and not $post) {
# Lone operator. Convert withdrawal into repetition.
if ($last->{is_withdrawal}) {
$pre = abs $last->{amount};
$pre == int $pre or return REJECT, "Repeat only works on integers.";
$cart->delete($last->{user}, -1);
$item_replaced = 1;
} elsif (not $cart->size) {
return ABORT, "Can't repeat an empty transaction.";
}
}
if ($pre) {
$pre = abs $pre; # withdrawal is negative
return REJECT, $err_limit if $pre > $limit;
$cart->add(undef, 0, "Next product repeated $pre times", { _repeat => abs $pre });
return ACCEPT;
}
return REJECT, $err_stacked if $last->{_repeated};
return "Multiply previous product by", \&repeat;
}
sub repeat {
my ($self, $cart, $arg) = @_;
$arg =~ /^\d+$/ and $arg > 0
or return REJECT, "Invalid value.";
return REJECT, $err_limit if $arg > $limit;
my @items = $cart->select_items;
my $last = $items[-1];
_do_repeat($cart, $last, $arg);
return ACCEPT;
}
sub hook_added {
my ($self, $cart, $user, $item) = @_;
$cart->size >= 2 or return;
my @items = $cart->select_items;
my @planned = $cart->select_items('_repeat');
return ABORT, $err_multi if $cart->is_multi_user and @planned;
return ABORT, "Multiple repeats queued; I'm confused." if @planned > 1;
return if not @planned;
return ABORT, $err_pfand if grep $_->{is_pfand}, @items;
for my $i (0 .. $#items - 1) {
my $item = $items[$i];
$item->{_repeat} or next;
my $next = $items[$i + 1];
return ABORT, $err_stacked if $next->{_repeat};
my $num = $item->{_repeat};
$cart->delete($item->{user}, $i);
_do_repeat($cart, $next, $num);
return;
}
}