Further update to new ledger-like internals

This commit is contained in:
Juerd Waalboer 2019-11-05 00:57:39 +01:00
parent 5a7a7184dd
commit 1cbc906f1e
22 changed files with 153 additions and 149 deletions

View file

@ -19,14 +19,16 @@ sub _call_old_hooks {
my $data = $entry->{attributes}; my $data = $entry->{attributes};
for ($entry, $entry->contras) { for (1 .. $entry->quantity) {
my $item = { for ($entry, $entry->contras) {
%$data, my $item = {
amount => $_->{amount}, %$data,
description => $_->{description}, amount => $_->{amount},
}; description => $_->{description},
};
RevBank::Plugins::call_hooks($hook, $self, $_->{user}, $item); RevBank::Plugins::call_hooks($hook, $self, $_->{user}, $item);
}
} }
} }
@ -118,14 +120,17 @@ sub checkout {
my %deltas; my %deltas;
for my $entry (@$entries) { for my $entry (@$entries) {
$entry->user($user); $entry->user($user);
$deltas{$_->{user}} += $_->{amount} for $entry, $entry->contras;
$deltas{$_->{user}} += $_->{amount} * $entry->quantity
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 %deltas) { for my $account (keys %deltas) {
RevBank::Users::update($account, $deltas{$account}, $transaction_id); RevBank::Users::update($account, $deltas{$account}, $transaction_id)
if $deltas{$account} != 0;
} }
RevBank::Plugins::call_hooks("checkout_done", $self, $user, $transaction_id); RevBank::Plugins::call_hooks("checkout_done", $self, $user, $transaction_id);
@ -142,24 +147,45 @@ sub select_items {
my @matches; my @matches;
for my $entry (@{ $self->{entries} }) { for my $entry (@{ $self->{entries} }) {
my %attributes = %{ $entry->{attributes} }; my %attributes = %{ $entry->{attributes} };
for my $item ($entry, $entry->contras) { for (1 .. $entry->quantity) {
push @matches, { %attributes, %$item } for my $item ($entry, $entry->contras) {
if @_ == 1 # No key or match given: match everything push @matches, { %attributes, %$item }
or @_ == 2 and $entry->has_attribute($key) # Just a key if @_ == 1 # No key or match given: match everything
or @_ == 2 and $entry->has_attribute($key) # Just a key
}
} }
} }
return @matches; return @matches;
} }
sub entries {
my ($self, $attribute) = @_;
my @entries = @{ $self->{entries} };
return grep $_->has_attribute($attribute), @entries if defined $attribute;
return @entries;
}
sub is_multi_user { sub is_multi_user {
Carp::carp("\$cart->is_multi_user is no longer supported, ignoring"); Carp::carp("\$cart->is_multi_user is no longer supported, ignoring");
} }
sub changed { sub changed {
my ($self) = @_; my ($self) = @_;
return delete $self->{changed};
my $changed = 0;
for my $entry ($self->entries('changed')) {
$entry->attribute('changed', undef);
$changed = 1;
}
$changed = 1 if delete $self->{changed};
return $changed;
}
sub sum {
my ($self) = @_;
return List::Util::sum(map $_->{amount} * $_->quantity, @{ $self->{entries} });
} }
1; 1;

View file

@ -34,6 +34,8 @@ sub add_contra {
amount => $amount, # should usually have opposite sign (+/-) amount => $amount, # should usually have opposite sign (+/-)
description => $description, description => $description,
}; };
$self->attribute('changed', 1);
} }
sub has_attribute { sub has_attribute {
@ -57,12 +59,13 @@ sub quantity {
if (defined $new) { if (defined $new) {
$new >= 0 or croak "Quantity must be positive"; $new >= 0 or croak "Quantity must be positive";
$$ref = $new; $$ref = $new;
$self->attribute('changed', 1);
} }
return $$ref; return $$ref;
} }
sub multiple { sub multiplied {
my ($self) = @_; my ($self) = @_;
return $self->{quantity} != 1; return $self->{quantity} != 1;
@ -81,7 +84,7 @@ sub as_printable {
$self->sanity_check; $self->sanity_check;
my @s; my @s;
push @s, $self->{quantity} . "x {" if $self->multiple; push @s, $self->{quantity} . "x {" if $self->multiplied;
# Normally, the implied sign is "+", and an "-" is only added for negative # Normally, the implied sign is "+", and an "-" is only added for negative
# numbers. Here, the implied sign is "-", and a "+" is only added for # numbers. Here, the implied sign is "-", and a "+" is only added for
@ -102,7 +105,7 @@ sub as_printable {
} }
push @s, "}" if $self->multiple; push @s, "}" if $self->multiplied;
return @s; return @s;
} }
@ -122,10 +125,10 @@ sub as_loggable {
my $description = my $description =
$quantity == 1 $quantity == 1
? $_->{description} ? $_->{description}
: sprintf("[%fx%.2f]", $quantity, $_->{amount}); : sprintf("[%sx %.2f] %s", $quantity, abs($_->{amount}), $_->{description});
push @s, sprintf( push @s, sprintf(
"%-12s %4s EUR %5.2f %s", "%-12s %4s EUR %5.2f # %s",
$_->{user}, $_->{user},
($total > 0 ? 'GAIN' : $total < 0 ? 'LOSE' : ''), ($total > 0 ? 'GAIN' : $total < 0 ? 'LOSE' : ''),
abs($total), abs($total),
@ -143,6 +146,7 @@ sub user {
croak "User can only be set once" if defined $self->{user}; croak "User can only be set once" if defined $self->{user};
$self->{user} = $new; $self->{user} = $new;
$self->attribute('changed', 1);
$_->{description} =~ s/\$you/$new/g for $self, @{ $self->{contras} }; $_->{description} =~ s/\$you/$new/g for $self, @{ $self->{contras} };
} }

View file

@ -24,8 +24,12 @@ 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;
say "Pending:";
$cart->display; $cart->display;
say "Enter username to pay/finish or 'abort' to abort.\n";
my $sum = $cart->sum;
my $what = $sum > 0 ? "add %.2f" : "pay %.2f";
say sprintf "Enter username to $what; type 'abort' to abort.\n", abs $sum;
} }
sub hook_abort { sub hook_abort {

View file

@ -7,23 +7,9 @@ HELP "deposit <amount>" => "Deposit into an account";
sub command :Tab(deposit) { sub command :Tab(deposit) {
my ($self, $cart, $command) = @_; my ($self, $cart, $command) = @_;
if ($command eq 'deposit') { $command eq 'deposit' or return NEXT;
return "Amount to deposit into your account", \&amount;
}
if ($cart->select_items('is_deposit')) { return "Amount to deposit into your account", \&amount;
# No other plugin recognised the input, so it must be a new user.
$self->{new_user} = $command;
my $x = RevBank::Plugin::adduser->can("command")
? "Please use \e[4madduser\e[0m instead."
: "Please enable the \e[4madduser\e[0m plugin.";
warn "Creating accounts with \e[4mdeposit\e[m is deprecated. $x\n";
return "Add new account for user '$command'?", \&create;
}
return NEXT;
} }
sub amount :Tab(13.37,42) { sub amount :Tab(13.37,42) {
@ -37,7 +23,7 @@ sub amount :Tab(13.37,42) {
return $message . "How are we receiving this EUR $amount?", \&how return $message . "How are we receiving this EUR $amount?", \&how
if keys %{ $self->{deposit_methods} }; if keys %{ $self->{deposit_methods} };
$cart->add(undef, +$self->{amount}, "Deposit", { is_deposit => 1 }); $cart->add(+$self->{amount}, "Deposit", { is_deposit => 1 });
return ACCEPT; return ACCEPT;
} }
@ -55,7 +41,7 @@ sub how :Tab(&how_tab) {
return shift @{ $how->{prompts} }, \&how_prompt; return shift @{ $how->{prompts} }, \&how_prompt;
} }
$cart->add(undef, +$self->{amount}, $how->{description}, { is_deposit => 1, method => $how->{_key} }); $cart->add(+$self->{amount}, $how->{description}, { is_deposit => 1, method => $how->{_key} });
return ACCEPT; return ACCEPT;
} }
@ -77,19 +63,6 @@ sub how_prompt {
my $desc = sprintf $how->{description}, @{ $how->{answers} }; my $desc = sprintf $how->{description}, @{ $how->{answers} };
$cart->add(undef, +$self->{amount}, $desc, { is_deposit => 1, method => $how->{_key} }); $cart->add(+$self->{amount}, $desc, { is_deposit => 1, method => $how->{_key} });
return ACCEPT; return ACCEPT;
} }
sub create :Tab(yes,no) {
my ($self, $cart, $yesno) = @_;
my $user = $self->{new_user};
if ($yesno eq "y" or $yesno eq "yes") {
RevBank::Users::create( $user );
$cart->checkout( $user );
return ACCEPT;
}
return ABORT;
}

View file

@ -28,9 +28,9 @@ sub command { NEXT }
sub hook_checkout { sub hook_checkout {
my ($class, $cart, $user, $transaction_id) = @_; my ($class, $cart, $user, $transaction_id) = @_;
my @items = $cart->select_items("is_deposit"); my @entries = $cart->entries("is_deposit");
my $amount = sum map $_->{amount}, grep $_->{method} eq "iban", @items; my $amount = sum map $_->{amount}, grep $_->attribute('method') eq 'iban', @entries;
if (defined $amount && $amount > 0) { if (defined $amount && $amount > 0) {
my $pid = open2 my $out, my $in, qw(qrencode -t ansiutf8 -m 2) my $pid = open2 my $out, my $in, qw(qrencode -t ansiutf8 -m 2)

View file

@ -2,29 +2,36 @@
HELP "dinnerbonus" => "Add fee for cooking supplies"; HELP "dinnerbonus" => "Add fee for cooking supplies";
my $bonus = 1.00;
sub command :Tab(kookbonus,dinnerbonus) { sub command :Tab(kookbonus,dinnerbonus) {
my ($self, $cart, $command) = @_; my ($self, $cart, $command) = @_;
my $bonus = 1.00; my @users = map $_->{user}, map $_->contras, $cart->entries('is_take');
$command eq 'kookbonus' or $command eq 'dinnerbonus' (@users and $command eq 'kookpotje') # common mistake promoted to feature
or $command eq 'kookbonus'
or $command eq 'dinnerbonus'
or return NEXT; or return NEXT;
my @users = grep !/^\$you$/, map $_->{user}, $cart->select_items @users or return REJECT, "$command requires a pending 'take'.";
or return REJECT, "$command requires a pending transaction.";
for my $user (@users) {
$cart->add( $user, -$bonus, "Kookbonus by \$you" );
}
my $users = join '/', @users; my $users = join '/', @users;
$cart->add( my $target = parse_user("kookpotje")
"kookpotje", or return ABORT, "User 'kookpotje' does not exist";
my $entry = $cart->add(0, "Kookbonus");
$entry->add_contra(
$target,
scalar @users * $bonus, scalar @users * $bonus,
"Kookbonus from $users by \$you" "Kookbonus from $users by \$you"
); );
for my $user (@users) {
$entry->add_contra( $user, -$bonus, "Kookbonus by \$you" );
}
return ACCEPT; return ACCEPT;
} }

View file

@ -41,8 +41,10 @@ sub reason :Tab(whatevah) {
my $user = parse_user($input); my $user = parse_user($input);
my $reason = $user ? "" : " ($input)"; my $reason = $user ? "" : " ($input)";
$cart->add(undef, -$amount, "Given to $benificiary" . $reason); $cart
$cart->add($benificiary, +$amount, "Received from \$you" . $reason); ->add(-$amount, "Given to $benificiary" . $reason)
->add_contra($benificiary, +$amount, "Received from \$you" . $reason);
$cart->checkout($user) if $user; $cart->checkout($user) if $user;
return ACCEPT; return ACCEPT;

View file

@ -49,7 +49,7 @@ sub hook_user_balance {
sub hook_checkout { sub hook_checkout {
my ($class, $cart, $username, $transaction_id) = @_; my ($class, $cart, $username, $transaction_id) = @_;
_log("CHECKOUT $transaction_id $_") for $cart->as_strings; _log("CHECKOUT $transaction_id $_") for map $_->as_loggable, $cart->entries;
} }
sub hook_register { sub hook_register {

View file

@ -37,8 +37,9 @@ sub command :Tab(market,&tab) {
my $space = parse_amount($product->{ space }) or return NEXT; my $space = parse_amount($product->{ space }) or return NEXT;
my $description = $product->{description}; my $description = $product->{description};
$cart->add(undef, -($seller + $space), $description,{product_id=>$command}); $cart
$cart->add($username, 0+$seller, "\$you bought $description") ->add(-($seller + $space), "$description (sold by $username)", {product_id=>$command})
->add_contra($username, 0+$seller, "\$you bought $description")
if 0+$seller; if 0+$seller;
return ACCEPT; return ACCEPT;
} }

View file

@ -29,7 +29,7 @@ sub product :Tab(&tab) {
my $pfand = _read_pfand->{ $product }; my $pfand = _read_pfand->{ $product };
if ($pfand) { if ($pfand) {
$cart->add(undef, +$pfand, "Pfand zurueck", { is_return => 1 }); $cart->add(+$pfand, "Pfand zurueck", { is_return => 1 });
} else { } else {
say "$product: Kein Pfand"; say "$product: Kein Pfand";
} }
@ -40,14 +40,15 @@ sub tab {
return keys %{ _read_pfand() }; return keys %{ _read_pfand() };
} }
sub hook_add { sub hook_add_entry {
my ($class, $cart, $user, $item) = @_; my ($class, $cart, $entry) = @_;
return if defined $user; return if $entry->has_attribute('is_return');
return if exists $item->{is_return}; return if not $entry->has_attribute('product_id');
return if not exists $item->{product_id};
my $pfand = _read_pfand->{ $item->{product_id} } or return; my $pfand = _read_pfand->{ $entry->attribute('product_id') } or return;
$cart->add(undef, -$pfand, "Pfand", { is_pfand => 1 }); $cart->add(-$pfand, "Pfand", { is_pfand => 1 });
return;
} }

View file

@ -35,7 +35,6 @@ sub command :Tab(edit,&tab) {
my $price = parse_amount( $product->{price} ) or return NEXT; my $price = parse_amount( $product->{price} ) or return NEXT;
$cart->add( $cart->add(
undef,
-$price, -$price,
$product->{description}, $product->{description},
{ product_id => $product->{id} } { product_id => $product->{id} }

View file

@ -3,7 +3,6 @@
HELP "*<N>, x<N>, <N>x, <N>*" => "Repeat previous/next product N times"; HELP "*<N>, x<N>, <N>x, <N>*" => "Repeat previous/next product N times";
my $err_stacked = "Stacked repetition is not supported."; 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 $err_pfand = "Plugins 'pfand' and 'repeat' cannot be combined.";
my $limit = 24; my $limit = 24;
@ -22,23 +21,21 @@ sub _do_repeat {
sub command { sub command {
my ($self, $cart, $command) = @_; my ($self, $cart, $command) = @_;
my @items = $cart->select_items; return ABORT, $err_pfand if $cart->entries('is_pfand');
my $last = $items[-1];
return ABORT, $err_pfand if grep $_->{is_pfand}, @items;
my ($pre, $post) = $command =~ /^(\d+)?[x*](\d+)?$/ my ($pre, $post) = $command =~ /^(\d+)?[x*](\d+)?$/
or return NEXT; or return NEXT;
my $last = ($cart->entries)[-1];
return NEXT if $pre and $post; # 123x123 -> invalid syntax return NEXT if $pre and $post; # 123x123 -> invalid syntax
if ($post) { if ($post) {
return REJECT, $err_multi if $cart->is_multi_user;
return REJECT, $err_limit if $post > $limit; return REJECT, $err_limit if $post > $limit;
return ABORT, "Can't repeat an empty transaction." if not $cart->size; return ABORT, "Can't repeat an empty transaction." if not $cart->size;
return REJECT, $err_stacked if $last->{_repeated}; return REJECT, $err_stacked if $last->multiplied;
_do_repeat($cart, $last, $post); $last->quantity($post);
return ACCEPT; return ACCEPT;
} }
@ -47,10 +44,10 @@ sub command {
if (not $pre and not $post) { if (not $pre and not $post) {
# Lone operator. Convert withdrawal into repetition. # Lone operator. Convert withdrawal into repetition.
if ($last->{is_withdrawal}) { if ($last->has_attribute('is_withdrawal')) {
$pre = abs $last->{amount}; $pre = abs $last->{amount};
$pre == int $pre or return REJECT, "Repeat only works on integers."; $pre == int $pre or return REJECT, "Repeat only works on integers.";
$cart->delete($last->{user}, -1); $cart->delete($last);
$item_replaced = 1; $item_replaced = 1;
} elsif (not $cart->size) { } elsif (not $cart->size) {
return ABORT, "Can't repeat an empty transaction."; return ABORT, "Can't repeat an empty transaction.";
@ -61,11 +58,11 @@ sub command {
$pre = abs $pre; # withdrawal is negative $pre = abs $pre; # withdrawal is negative
return REJECT, $err_limit if $pre > $limit; return REJECT, $err_limit if $pre > $limit;
$cart->add(undef, 0, "Next product repeated $pre times", { _repeat => abs $pre }); $cart->add(0, "Next product repeated $pre times", { _repeat => abs $pre });
return ACCEPT; return ACCEPT;
} }
return REJECT, $err_stacked if $last->{_repeated}; return REJECT, $err_stacked if $last->multiplied;
return "Multiply previous product by", \&repeat; return "Multiply previous product by", \&repeat;
} }
@ -77,39 +74,24 @@ sub repeat {
return REJECT, $err_limit if $arg > $limit; return REJECT, $err_limit if $arg > $limit;
my @items = $cart->select_items; ($cart->entries)[-1]->quantity($arg);
my $last = $items[-1];
_do_repeat($cart, $last, $arg);
return ACCEPT; return ACCEPT;
} }
sub hook_added { sub hook_added_entry {
my ($self, $cart, $user, $item) = @_; my ($self, $cart, $entry) = @_;
$cart->size >= 2 or return; $cart->size >= 2 or return;
my @items = $cart->select_items; my @entries = $cart->entries;
my @planned = $cart->select_items('_repeat'); my @planned = $cart->entries('_repeat');
my @repeated = $cart->select_items('_repeated'); my @repeated = grep $_->multiplied, $cart->entries;
return ABORT, $err_multi if $cart->is_multi_user and @planned || @repeated;
return ABORT, "Multiple repeats queued; I'm confused." if @planned > 1; return ABORT, "Multiple repeats queued; I'm confused." if @planned > 1;
return if not @planned; return if not @planned;
return ABORT, $err_pfand if grep $_->{is_pfand}, @items; return ABORT, $err_pfand if $cart->entries('is_pfand');
for my $i (0 .. $#items - 1) { my $num = $planned[0]->attribute('_repeat');
my $item = $items[$i];
$item->{_repeat} or next;
my $next = $items[$i + 1]; $cart->delete($planned[0]);
$entries[-1]->quantity($num);
return ABORT, $err_stacked if $next->{_repeat};
my $num = $item->{_repeat};
$cart->delete($item->{user}, $i);
_do_repeat($cart, $next, $num);
return;
}
} }

View file

@ -19,7 +19,6 @@ sub data {
my ($self, $cart, $input) = @_; my ($self, $cart, $input) = @_;
$cart->add( $cart->add(
undef,
-0.07, -0.07,
"Barcode <$input>", "Barcode <$input>",
{ is_barcode => 1, barcode_data => $input } { is_barcode => 1, barcode_data => $input }
@ -32,15 +31,15 @@ sub hook_checkout {
my ($class, $cart, $username, $transaction_id) = @_; my ($class, $cart, $username, $transaction_id) = @_;
my @barcodes; my @barcodes;
for my $item ($cart->select_items('is_barcode')) { for my $entry ($cart->entries('is_barcode')) {
push @barcodes, $item->{barcode_data}; push @barcodes, ($entry->attribute('barcode_data')) x $entry->quantity;
} }
if (@barcodes) { if (@barcodes) {
print "\nCheck the following:\n 1. label tape is 12 mm\n 2. printer is on\n 3. wifi is enabled and connected\n\nPress enter to continue."; print "\nCheck the following:\n 1. label tape is 12 mm\n 2. printer is on\n 3. wifi is enabled and connected\n\nPress enter to continue.";
readline STDIN; readline STDIN;
my $printjob = ""; my $printjob = "";
open my $bcgen, "-|", "/home/bar/revlabel/barcode.pl", @barcodes open my $bcgen, "-|", "/home/bar/revlabel/barcode.pl", @barcodes
or warn "Could not open script 1"; or warn "Could not open script 1";

View file

@ -11,7 +11,7 @@ sub command :Tab(BOUNTY1,BOUNTY2,BOUNTY3,BOUNTY4) {
my ($self, $cart, $command) = @_; my ($self, $cart, $command) = @_;
if ($command =~ /BOUNTY(\d+)/) { if ($command =~ /BOUNTY(\d+)/) {
$cart->add(undef, +$bounties{$1}[0], $bounties{$1}[1]); $cart->add(+$bounties{$1}[0], $bounties{$1}[1]);
return ACCEPT; return ACCEPT;
} }

View file

@ -7,7 +7,7 @@ sub command { NEXT }
sub hook_checkout { sub hook_checkout {
my ($class, $cart, $user, $transaction_id) = @_; my ($class, $cart, $user, $transaction_id) = @_;
my $filename = "revbank.sales"; my $filename = "revbank.sales";
my @items = $cart->select_items('product_id') or return; my @entries = $cart->entries('product_id') or return;
my %already_retained; my %already_retained;
my %stats = do { my %stats = do {
@ -17,9 +17,9 @@ sub hook_checkout {
: () : ()
}; };
$stats{ $_->{product_id} }++ for @items; $stats{ $_->attribute('product_id') } += $_->quantity for @entries;
for (@items) { for (@entries) {
my $product = $_->{product_id}; my $product = $_->{product_id};
publish "revspace/bank/sale" => $product; publish "revspace/bank/sale" => $product;

View file

@ -6,7 +6,7 @@ HELP "split <account>..." => "Split the bill with others";
sub _select_split { sub _select_split {
my ($cart) = @_; my ($cart) = @_;
grep $_->{amount} < 0, grep $_->{user} eq '$you', $cart->select_items grep $_->{amount} < 0, $cart->entries
} }
sub command :Tab(take,steal,split) { sub command :Tab(take,steal,split) {
@ -36,12 +36,12 @@ sub arg :Tab(USERS) {
my $total = sprintf "%.2f", @$users * $each; my $total = sprintf "%.2f", @$users * $each;
my $desc = join " + ", map $_->{description}, _select_split($cart); my $desc = join " + ", map $_->{description}, _select_split($cart);
for my $user (@$users) {
$cart->add( $user, -$each, "Taken by \$you (Split: $desc)" );
}
my $users = join '/', @$users; my $users = join '/', @$users;
$cart->add( undef, $total, "Taken from $users (Split: $desc)" ); my $entry = $cart->add($total, "Taken from $users (Split: $desc)" );
for my $user (@$users) {
$entry->add_contra( $user, -$each, "Taken by \$you (Split: $desc)" );
}
return ACCEPT; return ACCEPT;
} }

View file

@ -21,7 +21,7 @@ sub hook_checkout {
? "revbank.voorraad" ? "revbank.voorraad"
: "revbank.stock"; : "revbank.stock";
my @items = $cart->select_items('product_id') or return; my @entries = $cart->entries('product_id') or return;
my %stock = do { my %stock = do {
my $in; my $in;
@ -30,7 +30,7 @@ sub hook_checkout {
: () : ()
}; };
$stock{ $_->{product_id} }-- for @items; $stock{ $_->attribute('product_id') } -= $_->quantity for @entries;
open my $out, '>', "$filename.$$" or warn "$filename.$$: $!"; open my $out, '>', "$filename.$$" or warn "$filename.$$: $!";
printf {$out} "%-16s %+9d\n", $_, $stock{$_} for sort keys %stock; printf {$out} "%-16s %+9d\n", $_, $stock{$_} for sort keys %stock;

View file

@ -59,12 +59,11 @@ sub reason :Tab(bbq,NOABORT) { # finish
my $each = $self->{each}; my $each = $self->{each};
my $total = $self->{total}; my $total = $self->{total};
for my $user (@users) {
$cart->add( $user, -$each, "Taken by \$you ($reason)" );
}
my $users = join '/', @users; my $users = join '/', @users;
$cart->add( undef, $total, "Taken from $users ($reason)" ); my $entry = $cart->add($total, "Taken from $users ($reason)", { is_take => 1 });
for my $user (@users) {
$entry->add_contra( $user, -$each, "Taken by \$you ($reason)" );
}
return ACCEPT; return ACCEPT;
} }

View file

@ -19,10 +19,18 @@ sub undo {
open my $in, '<', $filename or die "$filename: $!"; open my $in, '<', $filename or die "$filename: $!";
open my $out, '>', "$filename.$$" or die "$filename.$$: $!"; open my $out, '>', "$filename.$$" or die "$filename.$$: $!";
my $description = "Undo $tid";
my $entry;
while (defined(my $line = readline $in)) { while (defined(my $line = readline $in)) {
if ($line =~ /^\Q$tid\E\s/) { if ($line =~ /^\Q$tid\E\s/) {
my (undef, $user, $delta) = split " ", $line; my (undef, $user, $delta) = split " ", $line;
$cart->add($user, $delta, "Undo $tid");
$entry ||= $cart->add(0, $description);
$entry->{FORCE} = 1;
$entry->add_contra($user, $delta, "Undo $tid");
} else { } else {
print {$out} $line; print {$out} $line;
} }
@ -31,7 +39,7 @@ sub undo {
close $out or die $!; close $out or die $!;
if ($cart->size) { if ($cart->size) {
rename "$filename.$$", $filename or die $!; rename "$filename.$$", $filename or die $!;
$cart->checkout(); $cart->checkout('**UNDO**');
} else { } else {
return ABORT, "Transaction ID '$tid' not found in undo log."; return ABORT, "Transaction ID '$tid' not found in undo log.";
} }

View file

@ -15,7 +15,7 @@ sub amount {
$self->{amount} = parse_amount($arg) or return REJECT, "Invalid amount."; $self->{amount} = parse_amount($arg) or return REJECT, "Invalid amount.";
if ($self->{command} eq 'donate') { if ($self->{command} eq 'donate') {
$cart->add(undef, -$self->{amount}, "Donation (THANK YOU!)"); $cart->add(-$self->{amount}, "Donation (THANK YOU!)");
return ACCEPT; return ACCEPT;
} }
@ -24,7 +24,7 @@ sub amount {
sub description { sub description {
my ($self, $cart, $desc) = @_; my ($self, $cart, $desc) = @_;
$cart->add(undef, -$self->{amount}, $desc); $cart->add(-$self->{amount}, $desc);
return ACCEPT; return ACCEPT;
} }

View file

@ -31,13 +31,12 @@ sub _read_warnings {
sub command { NEXT } sub command { NEXT }
sub hook_add { sub hook_add_entry {
my ($class, $cart, $user, $item) = @_; my ($class, $cart, $entry) = @_;
return if defined $user; # skip market items return if not $entry->has_attribute('product_id'); # skip unlisted, deposit, give, take
return if not exists $item->{product_id}; # skip unlisted, deposit, give, take
my @warnings = map { my @warnings = map {
$_->( $item->{product_id}, $item->{description} ) $_->( $entry->attribute('product_id'), $entry->{description} )
} _read_warnings; } _read_warnings;
return if not @warnings; return if not @warnings;

View file

@ -8,7 +8,7 @@ sub command {
my $amount = parse_amount($command); my $amount = parse_amount($command);
defined $amount or return NEXT; defined $amount or return NEXT;
$cart->add(undef, -$amount, "Withdrawal or unlisted product", $cart->add(-$amount, "Withdrawal or unlisted product",
{ is_withdrawal => 1 }); { is_withdrawal => 1 });
return ACCEPT; return ACCEPT;