revbank/plugins/undo
Juerd Waalboer ca03cb95d4 New plugin statiegeld_tokens
Activating statiegeld_tokens will limit the use of the statiegeld plugin
for container deposit refunds to what was bought at this venue.

Still needs documentation.

Changes to 'statiegeld' and 'undo' were made to support the new
plugin, specifically:

- metadata (attributes) added in $cart->add, for the statiegeld_tokens
  plugin to use.
- statiegeld plugin now shares a global variable (configuration).
- undo can now be rolled back during hook_checkout.
2023-01-16 03:08:42 +01:00

101 lines
2.8 KiB
Perl

#!perl
HELP1 "undo <transactionID>" => "Undo a transaction";
my $filename = ".revbank.undo";
my @TAB;
sub command :Tab(undo) ($self, $cart, $command, @) {
$command eq 'undo' or return NEXT;
$cart->size and return REJECT, "Undo is not available mid-transaction.";
my @log;
for my $line (slurp $filename) {
my ($tid, $user, $delta, $dt) = split " ", $line;
if (@log and $log[-1]{tid} eq $tid) {
push @{ $log[-1]{deltas} }, [ $user, $delta ];
} else {
push @log, { tid => $tid, dt => $dt, deltas => [ [ $user, $delta ] ] };
}
}
@TAB = ();
my $menu = "";
my $max = @log < 15 ? @log : 15;
for my $txn (@log[-$max .. -1]) {
$menu .= "ID: $txn->{tid} $txn->{dt} " . join(", ",
map { sprintf "%s:%+.2f", @$_ } @{ $txn->{deltas} }
) . "\n";
push @TAB, $txn->{tid};
}
return $menu . "Transaction ID", \&undo;
}
sub tab { @TAB }
my $doing_undo = 0; # Ugly but works, just like the rest of this plugin
sub undo :Tab(&tab) ($self, $cart, $tid, @) {
my $description = "Undo $tid";
my $entry;
my $found = 0;
my $aborted = 0;
with_lock {
my $backup = "$filename.bak.$$";
spurt $backup, slurp $filename; # copy for rollback
# Immediately remove from file, to avoid double undo when something
# crashes.
rewrite $filename, sub($line) {
if ($line =~ /^\Q$tid\E\s/) {
my (undef, $user, $delta) = split " ", $line;
$entry ||= $cart->add(0, $description, { undo_transaction_id => $tid });
$entry->{FORCE_UNBALANCED} = 1;
$entry->add_contra($user, $delta, "Undo $tid");
return undef; # remove line
} else {
return $line;
}
};
if ($cart->size) {
$found = 1;
$doing_undo = 1; # don't allow undoing undos
eval { $cart->checkout('-undo') };
if ($@ and $@ =~ "ROLLBACK_UNDO") {
# Undo the undo... :)
spurt $filename, slurp $backup;
# can't 'return ABORT' here; it would return from with_lock
$aborted = 1;
} elsif ($@) {
# Re-throw exception
die "(undo file BACKUP at $backup.)\n$@";
} else {
unlink $backup;
}
$doing_undo = 0;
}
};
return ABORT, "Undo prohibited." if $aborted;
return ACCEPT if $found;
return ABORT, "Transaction ID '$tid' not found in undo log.";
}
sub hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @) {
return if $doing_undo; # don't allow undoing undos
append $filename, join(" ", $transaction_id, $username, -$delta, now()), "\n";
}