undo: assert undoability instead of rolling back invalid undo
Making use of other recent changes, like that it's now safe to throw exceptions during hook_checkout_prepare to abort the transaction.
This commit is contained in:
parent
6b04ecc256
commit
f4d3b7fd5c
2 changed files with 33 additions and 54 deletions
|
@ -115,7 +115,7 @@ sub _warn($message) {
|
||||||
warn "\e[31;1mSorry,\e[0m $message\n";
|
warn "\e[31;1mSorry,\e[0m $message\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _handle_undo($cart) {
|
sub hook_undo($class, $cart) {
|
||||||
# Undoing properly is hard. We can easily void tokens, but we can't restore
|
# Undoing properly is hard. We can easily void tokens, but we can't restore
|
||||||
# them. That would requires duplicating all of the undo logic that exists
|
# them. That would requires duplicating all of the undo logic that exists
|
||||||
# for account balances, but for tokens. Too much work for something that I
|
# for account balances, but for tokens. Too much work for something that I
|
||||||
|
@ -127,10 +127,13 @@ sub _handle_undo($cart) {
|
||||||
next if $contra->{amount} < 0;
|
next if $contra->{amount} < 0;
|
||||||
next if List::Util::none { $contra->{user} eq $_ } _addon_accounts;
|
next if List::Util::none { $contra->{user} eq $_ } _addon_accounts;
|
||||||
|
|
||||||
_warn "deposit refunds cannot be undone.";
|
return ABORT, "Sorry, deposit refunds cannot be undone.";
|
||||||
die "ROLLBACK_UNDO";
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _handle_undo($cart) {
|
||||||
|
for my $entry ($cart->entries) {
|
||||||
# Undo buying: void specific tokens
|
# Undo buying: void specific tokens
|
||||||
my $undo_tid = $entry->attribute('undo_transaction_id')
|
my $undo_tid = $entry->attribute('undo_transaction_id')
|
||||||
or die "Plugin error: broken '-undo' transaction";
|
or die "Plugin error: broken '-undo' transaction";
|
||||||
|
|
78
plugins/undo
78
plugins/undo
|
@ -6,11 +6,6 @@ my $filename = ".revbank.undo";
|
||||||
|
|
||||||
my @TAB;
|
my @TAB;
|
||||||
|
|
||||||
{
|
|
||||||
package RevBank::Plugin::undo::RollBackUndo;
|
|
||||||
sub new($class) { return bless [], $class; }
|
|
||||||
}
|
|
||||||
|
|
||||||
sub command :Tab(undo) ($self, $cart, $command, @) {
|
sub command :Tab(undo) ($self, $cart, $command, @) {
|
||||||
$command eq 'undo' or return NEXT;
|
$command eq 'undo' or return NEXT;
|
||||||
|
|
||||||
|
@ -43,7 +38,7 @@ sub command :Tab(undo) ($self, $cart, $command, @) {
|
||||||
|
|
||||||
sub tab { @TAB }
|
sub tab { @TAB }
|
||||||
|
|
||||||
my $doing_undo = 0; # Ugly but works, just like the rest of this plugin
|
our $doing_undo = 0; # Ugly but works, just like the rest of this plugin
|
||||||
|
|
||||||
sub undo :Tab(&tab) ($self, $cart, $tid, @) {
|
sub undo :Tab(&tab) ($self, $cart, $tid, @) {
|
||||||
my $description = "Undo $tid";
|
my $description = "Undo $tid";
|
||||||
|
@ -51,13 +46,8 @@ sub undo :Tab(&tab) ($self, $cart, $tid, @) {
|
||||||
my $found = 0;
|
my $found = 0;
|
||||||
my $aborted = 0;
|
my $aborted = 0;
|
||||||
|
|
||||||
with_lock {
|
return with_lock {
|
||||||
my $backup = "$filename.bak.$$";
|
for my $line (slurp $filename) {
|
||||||
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/) {
|
if ($line =~ /^\Q$tid\E\s/) {
|
||||||
my (undef, $user, $delta) = split " ", $line;
|
my (undef, $user, $delta) = split " ", $line;
|
||||||
|
|
||||||
|
@ -65,49 +55,35 @@ sub undo :Tab(&tab) ($self, $cart, $tid, @) {
|
||||||
$entry->{FORCE_UNBALANCED} = 1;
|
$entry->{FORCE_UNBALANCED} = 1;
|
||||||
|
|
||||||
$entry->add_contra($user, $delta, "Undo $tid");
|
$entry->add_contra($user, $delta, "Undo $tid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart->size or return ABORT, "Transaction ID '$tid' not found in undo log.";
|
||||||
|
|
||||||
|
call_hooks("undo", $cart) or return ABORT;
|
||||||
|
|
||||||
|
local $doing_undo = 1; # don't allow undoing undos
|
||||||
|
$cart->checkout('-undo');
|
||||||
|
|
||||||
|
return ACCEPT;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) {
|
||||||
|
$username eq '-undo' or return;
|
||||||
|
|
||||||
|
for my $entry ($cart->entries) {
|
||||||
|
my $undo_tid = $entry->attribute('undo_transaction_id')
|
||||||
|
or die "Plugin error: broken '-undo' transaction";
|
||||||
|
|
||||||
|
rewrite $filename, sub($line) {
|
||||||
|
if ($line =~ /^\Q$undo_tid\E\s/) {
|
||||||
return undef; # remove line
|
return undef; # remove line
|
||||||
} else {
|
} else {
|
||||||
return $line;
|
return $line;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
if ($cart->size) {
|
|
||||||
$found = 1;
|
|
||||||
$doing_undo = 1; # don't allow undoing undos
|
|
||||||
|
|
||||||
eval { $cart->checkout('-undo') };
|
|
||||||
|
|
||||||
if ($@ isa RevBank::Plugin::undo::RollbackUndo) {
|
|
||||||
# Undo the undo... :)
|
|
||||||
spurt $filename, slurp $backup;
|
|
||||||
|
|
||||||
# can't 'return ABORT' here; it would return from with_lock
|
|
||||||
$aborted = 1;
|
|
||||||
} elsif ($@ isa RevBank::Cart::CheckoutProhibited) {
|
|
||||||
my $reason = $@->reason;
|
|
||||||
|
|
||||||
# Undo the undo... :)
|
|
||||||
spurt $filename, slurp $backup;
|
|
||||||
|
|
||||||
$aborted = 1;
|
|
||||||
warn "$reason\n";
|
|
||||||
} elsif ($@ and ref $@) {
|
|
||||||
# Re-throw exception object
|
|
||||||
die $@;
|
|
||||||
} elsif ($@) {
|
|
||||||
# Re-throw exception string
|
|
||||||
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, @) {
|
sub hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue