revbank/plugins/repeat
Juerd Waalboer f1389c8c5c Fix bug: would eat its own queued entry, resulting in no-op
It would work for the first item because the size >= 2 guard prevented the rest
of the sub from running.
2019-11-05 05:42:54 +01:00

89 lines
2.5 KiB
Perl

#!perl
HELP "*<N>, x<N>, <N>x, <N>*" => "Repeat previous/next product N times";
my $err_stacked = "Stacked repetition is not supported.";
my $err_pfand = "Plugins 'pfand' and 'repeat' cannot be combined.";
my $limit = 24;
my $err_limit = "Repetition is limited at $limit items.";
sub command {
my ($self, $cart, $command) = @_;
return ABORT, $err_pfand if $cart->entries('is_pfand');
my ($pre, $post) = $command =~ /^(\d+)?[x*](\d+)?$/
or return NEXT;
my $last = ($cart->entries)[-1];
return NEXT if $pre and $post; # 123x123 -> invalid syntax
if ($post) {
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->multiplied;
$last->quantity($post);
return ACCEPT;
}
if (not $pre and not $post) {
# Lone operator. Convert withdrawal into repetition.
if ($last->has_attribute('is_withdrawal')) {
$pre = abs $last->{amount};
$pre == int $pre or return REJECT, "Repeat only works on integers.";
$cart->delete($last);
} 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(0, "? (The next thing you add will be multiplied.)", { _repeat => 1 })
->quantity($pre);
return ACCEPT;
}
return REJECT, $err_stacked if $last->multiplied;
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;
($cart->entries)[-1]->quantity($arg);
return ACCEPT;
}
sub hook_added_entry {
my ($self, $cart, $entry) = @_;
$cart->size >= 2 or return;
my @entries = $cart->entries;
my @planned = $cart->entries('_repeat');
return if not @planned;
return ABORT, "Multiple repeats queued; I'm confused." if @planned > 1;
return ABORT, $err_pfand if $cart->entries('is_pfand');
return if $planned[0] == $entries[-1];
return ABORT, "Queued repeat is not the penultimate item; I'm confused"
if $entries[-2] != $planned[0];
my $num = $planned[0]->quantity;
$cart->delete($planned[0]);
$entries[-1]->quantity($num);
}