diff --git a/UPGRADING.md b/UPGRADING.md index 2552a0c..2d052e2 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -19,6 +19,57 @@ supports Perl versions down to 5.32 (2020), which is in Debian 11 "bullseye" and 12 "bookworm" becomes the new oldstable, RevBank will begin to require Perl 5.36 (2022). +# (2025-04-10) RevBank 9.0.0 + +In many places, the term 'user' has been replaced with the term 'account', to +more accurately describe the current state of RevBank, which has non-user +accounts in addition to user accounts. + +The term 'account' is now the generic term (visible and hidden accounts), but +'user' or 'username' is still used where only user accounts (visible accounts) +are valid. + +## Renamed hooks + +| Old name | New name | +|---------------------|------------------------| +| `hook_user_created` | `hook_account_created` | +| `hook_user_balance` | `hook_account_balance` | + +The new hooks are added in addition to the old ones. + +The old hooks will be removed in a future version, after 2027-05-01. + +## Renamed global identifiers + +| Old name | New name | +|-------------------------------|-------------------------------------| +| `RevBank::Users::assert_user` | `RevBank::Accounts::assert_account` | +| `RevBank::Users` | `RevBank::Accounts` | +| `$contra->{user}` | `$contra->{account}` | +| `$entry->user` | `$entry->account` | + +Custom plugins might be affected by this change, but most won't. + +`->{user}` was kept for read-only use, and will be removed after 2027-05-01. + +The old functions/method names are aliases for the new ones, and will be +removed after 2027-05-01. + +## Not renamed + +The following remain unchanged, as they only or mostly pertain to visible +accounts, which are primarily intended as user accounts: + +- `parse_user()` function +- `hook_user_info` +- `adduser` command +- `users` plugin + +The following remain unchanged (for now) because external scripts might break +if these were changed: +- `NEWUSER` in the log file + # (2024-12-26) RevBank 8.0.0 Another breaking change, another major version upgrade due to semantic versioning! diff --git a/lib/RevBank.pod b/lib/RevBank.pod index 7f0058c..e55ea52 100644 --- a/lib/RevBank.pod +++ b/lib/RevBank.pod @@ -10,7 +10,7 @@ Since version 2, RevBank is loosely modeled after C, which is an SMTP s RevBank is interactive and stateful. Global state is provided in the form of a "shopping cart", a L object, which represents the ongoing, unfinished, transaction. The terms "cart", "unfinished transaction", and "current transaction" generally all refer to the same thing in the context of RevBank. -In addition, RevBank provides the concept of accounts through L. There are user accounts and internal accounts; internal accounts are used as contra accounts for I, and are hidden from the user interface. Accounts only have a name, a balance, and some timestamps; things like transaction histories are provided by plugins. +In addition, RevBank provides the concept of accounts through L. There are user accounts and hidden accounts; hidden accounts are used as contra accounts for I, and are hidden from the user interface. Accounts only have a name, a balance, and some timestamps; things like transaction histories are provided by plugins. Notably, the RevBank core does B have any notion of "products". Support for buying products through RevBank is provided by plugins, like the included C and C plugins. It is easy to add another source of products by writing another plugin. A plugin contains arbitrary code and can do anything, including querying external resources. diff --git a/lib/RevBank/Users.pm b/lib/RevBank/Accounts.pm similarity index 51% rename from lib/RevBank/Users.pm rename to lib/RevBank/Accounts.pm index 227b6ce..de3631b 100644 --- a/lib/RevBank/Users.pm +++ b/lib/RevBank/Accounts.pm @@ -1,4 +1,4 @@ -package RevBank::Users; +package RevBank::Accounts; use v5.32; use warnings; @@ -12,7 +12,7 @@ use List::Util (); my $filename = "revbank.accounts"; sub _read() { - my @users; + my @accounts; for my $line (slurp $filename) { $line =~ /\S/ or next; # Not using RevBank::Prompt::split_input to keep parsing by external @@ -25,23 +25,23 @@ sub _read() { @split = split " ", $line, 2; } - push @users, \@split; + push @accounts, \@split; } - my %users; - for (@users) { + my %accounts; + for (@accounts) { my $name = lc $_->[0]; - exists $users{$name} and die "$filename: duplicate entry '$name'\n"; - $users{$name} = $_; + exists $accounts{$name} and die "$filename: duplicate entry '$name'\n"; + $accounts{$name} = $_; if ($name =~ s/^\*//) { # user-accessible special account: support without * prefix - exists $users{$name} and die "$filename: duplicate entry '$name'\n"; - $users{$name} = $_; + exists $accounts{$name} and die "$filename: duplicate entry '$name'\n"; + $accounts{$name} = $_; } } - return \%users; + return \%accounts; } sub names() { @@ -50,25 +50,26 @@ sub names() { return List::Util::uniqstr map $_->[0], values %{ _read() }; } -sub balance($username) { - return RevBank::Amount->parse_string( _read()->{ lc $username }->[1] ); +sub balance($account) { + return RevBank::Amount->parse_string( _read()->{ lc $account }->[1] ); } -sub since($username) { - return _read()->{ lc $username }->[3]; +sub since($account) { + return _read()->{ lc $account }->[3]; } -sub create($username) { - die "Account already exists" if exists _read()->{ lc $username }; +sub create($account) { + die "Account already exists" if exists _read()->{ lc $account }; my $now = now(); - append $filename, "$username 0.00 $now\n"; - RevBank::Plugins::call_hooks("user_created", $username); - return $username; + append $filename, "$account 0.00 $now\n"; + RevBank::Plugins::call_hooks("user_created", $account); # until 2027-05-01 + RevBank::Plugins::call_hooks("account_created", $account); + return $account; } -sub update($username, $delta, $transaction_id) { - my $account = assert_user($username) or die "No such user ($username)"; +sub update($account, $delta, $transaction_id) { + $account = assert_account($account); my $old = RevBank::Amount->new(0); my $new = RevBank::Amount->new(0); @@ -99,24 +100,28 @@ sub update($username, $delta, $transaction_id) { }; RevBank::Plugins::call_hooks( + # Backwards compatibility until 2027-05-01 "user_balance", $account, $old, $delta, $new, $transaction_id ); + RevBank::Plugins::call_hooks( + "account_balance", $account, $old, $delta, $new, $transaction_id + ); } -sub is_hidden($username) { - return $username =~ /^[-+]/; +sub is_hidden($account) { + return $account =~ /^[-+]/; } -sub is_special($username) { - return $username =~ /^[-+*]/; +sub is_special($account) { + return $account =~ /^[-+*]/; } sub parse_user($username, $allow_invalid = 0) { return undef if is_hidden($username); - my $users = _read(); + my $accounts = _read(); - my $user = $users->{ lc $username } or return undef; + my $user = $accounts->{ lc $username } or return undef; if ($user->[1] =~ /^!(.*)/) { warn "$username: Invalid account ($1).\n"; @@ -128,21 +133,32 @@ sub parse_user($username, $allow_invalid = 0) { return $user->[0]; } -sub assert_user($username) { - my $users = _read(); +sub assert_account($account) { + my $accounts = _read(); - my $user = $users->{ lc $username }; + my $account_info = $accounts->{ lc $account }; - if ($user) { - Carp::croak("Account $username can't be used") if not defined balance $username; - return $user->[0]; + if ($account) { + Carp::croak("Account $account can't be used") if not defined balance $account; + return $account_info->[0]; } - return create $username if is_hidden $username; + return create $account if is_hidden $account; - Carp::croak("No such user ($username)") + Carp::croak("No such user ($account)"); } +# Backwards compatibility until 2027-05-01 +*RevBank::Users::names = \&RevBank::Accounts::names; +*RevBank::Users::balance = \&RevBank::Accounts::balance; +*RevBank::Users::since = \&RevBank::Accounts::since; +*RevBank::Users::create = \&RevBank::Accounts::create; +*RevBank::Users::update = \&RevBank::Accounts::update; +*RevBank::Users::is_hidden = \&RevBank::Accounts::is_hidden; +*RevBank::Users::is_special = \&RevBank::Accounts::is_special; +*RevBank::Users::parse_user = \&RevBank::Accounts::parse_user; +*RevBank::Users::assert_user = \&RevBank::Accounts::assert_account; + 1; diff --git a/lib/RevBank/Users.pod b/lib/RevBank/Accounts.pod similarity index 77% rename from lib/RevBank/Users.pod rename to lib/RevBank/Accounts.pod index 75d0f44..e56a234 100644 --- a/lib/RevBank/Users.pod +++ b/lib/RevBank/Accounts.pod @@ -1,10 +1,10 @@ =head1 NAME -RevBank::Users - Banking and bookkeeping accounts +RevBank::Accounts - Banking and bookkeeping accounts =head1 DESCRIPTION -This package handles all accounts in RevBank. Accounts are called "users" because originally, RevBank only had user accounts. Today, RevBank does doubly-entry bookkeeping and has multiple account types to accommodate that. +This package handles all accounts in RevBank. RevBank does doubly-entry bookkeeping and has multiple account types to accommodate that. This package is where manipulation of C happens. @@ -16,6 +16,10 @@ This package is where manipulation of C happens. User accounts are typically made with the C command, and almost all interactions with RevBank will involve only user accounts, from the perspective of the user. +The name of a user account is called a I within RevBank. + +Any account that does not begin with one of the characters C<->, C<+>, or C<*>, is a user account. + =item * Hidden accounts The name of a hidden account begins with a C<-> or C<+> sign. These accounts are created automatically by plugins to provide the I part in I. @@ -100,9 +104,9 @@ Only the first two columns are mandatory. This makes migrating to RevBank very s =head2 Functions -Usernames are case preserving, but case insensitive. Account name arguments to functions are case insensitive, but return values use the canonical capitalization. +Account names are case preserving, but case insensitive. Account name arguments to functions are case insensitive, but return values use the canonical capitalization. -Anything that outputs a username should always run it through C or C. +Anything that outputs a username should always run it through C or C. =head3 names @@ -120,13 +124,13 @@ Returns the last used datetime of the account. Creates an account with that name and a balance of zero. The name must not already exist. -After updating the file, calls the C hook with the account name. +After updating the file, calls the C hook with the account name. =head3 update($name, $delta, $transaction_id) Given the relative change (C<$delta>), updates the user balance for an account. -After updating the file, calls the C hook with the account name, the old balance, the given delta, the new balance, and the transaction_id. +After updating the file, calls the C hook with the account name, the old balance, the given delta, the new balance, and the transaction_id. This function should not be used directly; instead, create a transaction via C and use C to ensure a balanced booking for proper double-entry bookkeeping. @@ -140,17 +144,19 @@ Returns true if the account is hidden (begins with C<+> or C<->), or user-access =head3 parse_user($username) -Returns the canonical account name if the user account exists, or undef if it does not exist. +Returns the canonical account name if the account exists and is not a hidden account, or undef otherwise. -=head3 assert_user($name) +=head3 assert_account($name) For a hidden account, returns the canonical account name, creating the account if it did not already exist. -For a non-hidden account, works like parse_user. +For a non-hidden account, returns the canonical account name if the account exists, or throws an exception if it does not exist. -=head1 CAVEATS +=head1 HISTORY -The identifiers can be confusing and most instances of C should probably be renamed to C. +Originally, RevBank had only user accounts, and the package was called C. When hidden (internal) accouns were added, account names were still always in variables called C<$user> or C<$username> even if they were hidden accounts and thus not accessible to users. In current RevBank, the term I is used as the generic thing, or I only in places where only user accounts (non-hidden accounts) are supported. + +This change took place in 2025, and some backwards compatibility will be kept until at least 2027-05-01. See UPGRADING.md for more information. =head1 AUTHOR diff --git a/lib/RevBank/Cart.pm b/lib/RevBank/Cart.pm index 7240413..6bfcf58 100644 --- a/lib/RevBank/Cart.pm +++ b/lib/RevBank/Cart.pm @@ -7,7 +7,7 @@ use experimental 'signatures'; # stable since v5.36 use Carp (); use List::Util (); use RevBank::Global; -use RevBank::Users; +use RevBank::Accounts; use RevBank::FileIO; use RevBank::Cart::Entry; @@ -84,11 +84,11 @@ sub prohibit_checkout($self, $bool, $reason) { } } -sub deltas($self, $user) { - my %deltas = ($user => RevBank::Amount->new(0)); +sub deltas($self, $account) { + my %deltas = ($account => RevBank::Amount->new(0)); for my $entry (@{ $self->{entries} }) { - $deltas{$_->{user}} += $_->{amount} * $entry->quantity + $deltas{$_->{account}} += $_->{amount} * $entry->quantity for $entry, $entry->contras; } @@ -96,7 +96,7 @@ sub deltas($self, $user) { } -sub checkout($self, $user) { +sub checkout($self, $account) { if ($self->{prohibited}) { die RevBank::Cart::CheckoutProhibited->new( "Cannot complete transaction: $self->{prohibited}" @@ -108,13 +108,13 @@ sub checkout($self, $user) { die "Refusing to finalize deficient transaction"; } - $user = RevBank::Users::assert_user($user); + $account = RevBank::Accounts::assert_account($account); my $entries = $self->{entries}; for my $entry (@$entries) { $entry->sanity_check; - $entry->user($user); + $entry->account($account); } RevBank::FileIO::with_lock { @@ -133,28 +133,28 @@ sub checkout($self, $user) { $transaction_id = time() - 1300000000; } - RevBank::Plugins::call_hooks("checkout_prepare", $self, $user, $transaction_id) + RevBank::Plugins::call_hooks("checkout_prepare", $self, $account, $transaction_id) or die "Refusing to finalize after failed checkout_prepare"; for my $entry (@$entries) { $entry->sanity_check; - $entry->user($user) if not $entry->user; + $entry->account($account) if not $entry->account; } RevBank::FileIO::spurt($fn, ++(my $next_id = $transaction_id)) unless $legacy_id; - RevBank::Plugins::call_hooks("checkout", $self, $user, $transaction_id); + RevBank::Plugins::call_hooks("checkout", $self, $account, $transaction_id); - my $deltas = $self->deltas($user); + my $deltas = $self->deltas($account); for my $account (reverse sort keys %$deltas) { # The reverse sort is a lazy way to make the "-" accounts come last, # which looks nicer with the "cash" plugin. - RevBank::Users::update($account, $deltas->{$account}, $transaction_id) + RevBank::Accounts::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, $account, $transaction_id); sleep 1; # look busy diff --git a/lib/RevBank/Cart/Entry.pm b/lib/RevBank/Cart/Entry.pm index 36d14f4..9d36f34 100644 --- a/lib/RevBank/Cart/Entry.pm +++ b/lib/RevBank/Cart/Entry.pm @@ -5,7 +5,7 @@ use warnings; use experimental 'signatures'; # stable since v5.36 use Carp qw(carp croak); -use RevBank::Users; +use RevBank::Accounts; use List::Util (); use Scalar::Util (); @@ -24,7 +24,7 @@ sub new($class, $amount, $description, $attributes = {}) { amount => $amount, # negative = pay, positive = add money description => $description, attributes => { %$attributes }, - user => undef, + account => undef, contras => [], caller => List::Util::first(sub { !/^RevBank::Cart/ }, map { (caller $_)[3] } 1..10) || (caller 1)[3], @@ -34,20 +34,21 @@ sub new($class, $amount, $description, $attributes = {}) { return bless $self, $class; } -sub add_contra($self, $user, $amount, $description, $display = undef) { +sub add_contra($self, $account, $amount, $description, $display = undef) { # $display should be given for either ALL or NONE of the contras, # with the exception of contras with $amount == 0.00; $amount = RevBank::Amount->parse_string($amount) if not ref $amount; - $user = RevBank::Users::assert_user($user); + $account = RevBank::Accounts::assert_account($account); - $description =~ s/\$you/$self->{user}/g if defined $self->{user}; + $description =~ s/\$you/$self->{account}/g if defined $self->{account}; push @{ $self->{contras} }, { - user => $user, - amount => $amount, # should usually have opposite sign (+/-) - description => $description, # contra user's perspective - display => $display, # interactive user's perspective + account => $account, + user => $account, # backwards compatibility until 2027-05-01 + amount => $amount, # should usually have opposite sign (+/-) + description => $description, # contra account's perspective + display => $display, # interactive user's perspective highlight => 1, }; @@ -133,9 +134,9 @@ sub as_printable($self) { for my $c (@{ $self->{contras} }) { my $description; my $amount = $self->{amount}; - my $hidden = RevBank::Users::is_hidden($c->{user}); + my $hidden = RevBank::Accounts::is_hidden($c->{account}); my $fromto = $c->{amount}->cents < 0 ? "<-" : "->"; - $fromto .= " $c->{user}"; + $fromto .= " $c->{account}"; if ($c->{display}) { $description = @@ -165,7 +166,7 @@ sub as_printable($self) { } sub as_loggable($self) { - croak "Loggable called before set_user" if not defined $self->{user}; + croak "Loggable called before set_account" if not defined $self->{account}; my $quantity = $self->{quantity}; @@ -180,7 +181,7 @@ sub as_loggable($self) { push @s, sprintf( "%-12s %4s %3d %6s # %s", - $_->{user}, + $_->{account}, ($total->cents > 0 ? 'GAIN' : $total->cents < 0 ? 'LOSE' : '===='), $quantity, $total->abs, @@ -191,17 +192,20 @@ sub as_loggable($self) { return @s; } -sub user($self, $new = undef) { +sub account($self, $new = undef) { if (defined $new) { - croak "User can only be set once" if defined $self->{user}; + croak "User can only be set once" if defined $self->{account}; - $self->{user} = $new; + $self->{account} = $new; + $self->{user} = $new; # backwards compatibility until 2027-05-01 $_->{description} =~ s/\$you/$new/g for $self, @{ $self->{contras} }; } - return $self->{user}; + return $self->{account}; } +*user = \&account; # backwards compatibility until 2027-05-01 + sub sanity_check($self) { my @contras = $self->contras; diff --git a/lib/RevBank/Global.pm b/lib/RevBank/Global.pm index c5240e6..b956b66 100644 --- a/lib/RevBank/Global.pm +++ b/lib/RevBank/Global.pm @@ -16,7 +16,7 @@ use RevBank::FileIO; sub import { require RevBank::Plugins; - require RevBank::Users; + require RevBank::Accounts; no strict 'refs'; my $caller = caller; *{"$caller\::ACCEPT"} = sub () { \1 }; @@ -30,7 +30,7 @@ sub import { *{"$caller\::rewrite"} = \&RevBank::FileIO::rewrite; *{"$caller\::append"} = \&RevBank::FileIO::append; *{"$caller\::with_lock"} = \&RevBank::FileIO::with_lock; - *{"$caller\::parse_user"} = \&RevBank::Users::parse_user; + *{"$caller\::parse_user"} = \&RevBank::Accounts::parse_user; *{"$caller\::parse_amount"} = sub ($amount) { defined $amount or return undef; length $amount or return undef; diff --git a/lib/RevBank/Global.pod b/lib/RevBank/Global.pod index a368029..282c44d 100644 --- a/lib/RevBank/Global.pod +++ b/lib/RevBank/Global.pod @@ -32,10 +32,10 @@ Commas are changed to periods so C<3,50> and C<3.50> both result in C<3.5>. =head2 parse_user($username) -See C in L. - Returns the canonical username, or undef if the account does not exist. +See C in L for the gory details. + =head1 AUTHOR Juerd Waalboer <#####@juerd.nl> diff --git a/lib/RevBank/Messages.pm b/lib/RevBank/Messages.pm index 7027efd..ae1f61c 100644 --- a/lib/RevBank/Messages.pm +++ b/lib/RevBank/Messages.pm @@ -12,7 +12,7 @@ use base 'RevBank::Plugin'; BEGIN { RevBank::Plugins::register("RevBank::Messages"); - *hidden = \&RevBank::Users::is_hidden; + *hidden = \&RevBank::Accounts::is_hidden; } @@ -41,7 +41,7 @@ sub hook_cart_changed($class, $cart, @) { } } -sub hook_checkout($class, $cart, $user, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { if ($cart->changed) { say "Done:"; $cart->display; @@ -66,8 +66,8 @@ sub hook_reject($class, $plugin, $reason, $abort, @) { say $abort ? $reason : "$reason Enter 'abort' to abort."; } -sub hook_user_balance($class, $username, $old, $delta, $new, @) { - return if hidden $username and not $ENV{REVBANK_DEBUG}; +sub hook_account_balance($class, $account, $old, $delta, $new, @) { + return if hidden $account and not $ENV{REVBANK_DEBUG}; my $sign = $delta->cents >= 0 ? '+' : '-'; my $rood = $new->cents < 0 ? '31;' : ''; @@ -75,13 +75,13 @@ sub hook_user_balance($class, $username, $old, $delta, $new, @) { my $warn = $new->cents < -2300 ? " \e[5;1m(!!)\e[0m" : ""; $_ = $_->string("+") for $old, $new; - printf "New balance for $username: $old $sign $abs = \e[${rood}1m$new\e[0m$warn\n", + printf "New balance for $account: $old $sign $abs = \e[${rood}1m$new\e[0m$warn\n", } -sub hook_user_created($class, $username, @) { - return if hidden $username and not $ENV{REVBANK_DEBUG}; +sub hook_account_created($class, $account, @) { + return if hidden $account and not $ENV{REVBANK_DEBUG}; - say "New account '$username' created."; + say "New account '$account' created."; } 1; diff --git a/lib/RevBank/Plugin.pm b/lib/RevBank/Plugin.pm index ce786c6..74f6e13 100644 --- a/lib/RevBank/Plugin.pm +++ b/lib/RevBank/Plugin.pm @@ -33,8 +33,8 @@ sub Tab($self, $method) { } if (delete $completions{USERS}) { - for my $name (RevBank::Users::names()) { - next if RevBank::Users::is_hidden($name); + for my $name (RevBank::Accounts::names()) { + next if RevBank::Accounts::is_hidden($name); $completions{ $name }++; $completions{ $1 }++ if $name =~ /^\*(.*)/; diff --git a/lib/RevBank/Plugins.pod b/lib/RevBank/Plugins.pod index 18c8a17..e64c9c3 100644 --- a/lib/RevBank/Plugins.pod +++ b/lib/RevBank/Plugins.pod @@ -145,10 +145,10 @@ Called when user input was given. C<$split_input> is a boolean that is true if the input will be split on whitespace, rather than treated as a whole. The input MAY be altered by the plugin. -=item hook_add($class, $cart, $user, $item, @) +=item hook_add($class, $cart, $account, $item, @) Called when something is added to the cart. Of course, like in C<< $cart->add ->>, C<$user> will be undef if the product is added for the current user. +>>, C<$account> will be undef if the product is added for the current user. C<$item> is a reference to a hash with the keys C, C and the metadata given in the C call. Changing the values changes the actual @@ -156,15 +156,15 @@ item going into the cart! Be careful to avoid infinite loops if you add new stuff. -=item hook_checkout_prepare($class, $cart, $user, $transaction_id, @) +=item hook_checkout_prepare($class, $cart, $account, $transaction_id, @) Called when the transaction is about to be processed. In this phase, the cart and its entries can still be manipulated. If the hook throws an exception, the transaction is aborted. -=item hook_checkout($class, $cart, $user, $transaction_id, @) +=item hook_checkout($class, $cart, $account, $transaction_id, @) Called when the transaction is finalized, before accounts are updated. The cart and cart entries must not be changed. -=item hook_checkout_done($class, $cart, $user, $transaction_id, @) +=item hook_checkout_done($class, $cart, $account, $transaction_id, @) Called when the transaction is finalized, after accounts were updated. @@ -181,13 +181,13 @@ Called when input was not recognised by any of the plugins. Called when a plugin fails. -=item hook_user_created($class, $username, @) +=item hook_account_created($class, $account, @) -Called when a new user account was created. +Called when a new account was created. -=item hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @) +=item hook_account_balance($class, $account, $old, $delta, $new, $transaction_id, @) -Called when a user account is updated. +Called when an account is updated. =item hook_products_changed($class, $changes, $mtime, @) diff --git a/plugins/adduser b/plugins/adduser index 3c9f83d..255ccef 100644 --- a/plugins/adduser +++ b/plugins/adduser @@ -34,7 +34,7 @@ sub username($self, $cart, $name, @) { if any sub { $_ eq $name }, $plugin->Tab('command'); } - RevBank::Users::create( $name ); + RevBank::Accounts::create( $name ); return ACCEPT; } diff --git a/plugins/cash b/plugins/cash index 201dd34..491efd6 100644 --- a/plugins/cash +++ b/plugins/cash @@ -31,13 +31,13 @@ sub command :Tab(cash) ($self, $cart, $command, @) { sub hook_cash($class, @) { printf "There should currently be (at least) %s in the cash box.\n", - -RevBank::Users::balance("-cash") || "0.00"; + -RevBank::Accounts::balance("-cash") || "0.00"; } our $suppress = 0; -sub hook_user_balance($class, $username, $old, $delta, $new, @) { - return if $username ne '-cash' or $delta->cents == 0; +sub hook_account_balance($class, $account, $old, $delta, $new, @) { + return if $account ne '-cash' or $delta->cents == 0; return if $suppress; # "-" accounts need to be inverted to display the intuitive value. @@ -55,7 +55,7 @@ sub hook_user_balance($class, $username, $old, $delta, $new, @) { my $confirm_prompt = "Type 'fix pls' to apply a permanent correction, or 'abort' to abort"; sub check($self, $cart, $arg, @) { - my $should = -RevBank::Users::balance("-cash") || parse_amount(0); + my $should = -RevBank::Accounts::balance("-cash") || parse_amount(0); my $have = parse_amount($arg); return REJECT, "Invalid amount" if not defined $have; @@ -94,7 +94,7 @@ sub confirm($self, $cart, $arg, @) { $cart->checkout('-expenses/discrepancies'); printf "\nDiscrepancy recorded; corrected cash box amount is %s.\n", - -RevBank::Users::balance("-cash") || "0.00"; + -RevBank::Accounts::balance("-cash") || "0.00"; return ACCEPT; } diff --git a/plugins/cash_drawer b/plugins/cash_drawer index 23888af..b287061 100644 --- a/plugins/cash_drawer +++ b/plugins/cash_drawer @@ -15,8 +15,8 @@ sub hook_cash { open_drawer(); } -sub hook_checkout($class, $cart, $user, $transaction_id, @) { - $user eq '-cash' or return; +sub hook_checkout($class, $cart, $account, $transaction_id, @) { + $account eq '-cash' or return; open_drawer(); } diff --git a/plugins/deposit_iban_qr b/plugins/deposit_iban_qr index bd63556..a93ad7b 100644 --- a/plugins/deposit_iban_qr +++ b/plugins/deposit_iban_qr @@ -23,7 +23,7 @@ use List::Util qw(sum); my $iban = "NL99ABCD1234567890"; my $beneficiary = "Account Name"; -sub hook_checkout($class, $cart, $user, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { my @entries = $cart->entries("is_deposit"); my $amount = sum map $_->{amount}, grep $_->attribute('method') eq 'iban', @entries; @@ -41,7 +41,7 @@ sub hook_checkout($class, $cart, $user, $transaction_id, @) { "EUR" . $amount, # Amount "", "", - "rb $user", + "rb $account", "", ); close $in; diff --git a/plugins/dinnerbonus b/plugins/dinnerbonus index 88b2523..c558388 100644 --- a/plugins/dinnerbonus +++ b/plugins/dinnerbonus @@ -5,7 +5,7 @@ HELP "dinnerbonus" => "Add fee for cooking supplies"; my $bonus = 1.00; sub command :Tab(kookbonus,dinnerbonus) ($self, $cart, $command, @) { - my @users = map $_->{user}, map $_->contras, $cart->entries('is_take'); + my @users = map $_->{account}, map $_->contras, $cart->entries('is_take'); (@users and $command eq 'kookpotje') # common mistake promoted to feature or $command eq 'kookbonus' diff --git a/plugins/grandtotal b/plugins/grandtotal index 437068b..ec1b163 100644 --- a/plugins/grandtotal +++ b/plugins/grandtotal @@ -9,8 +9,8 @@ sub command :Tab(grandtotal) ($self, $cart, $command, @) { my $neg = 0; for my $line (slurp 'revbank.accounts') { - my ($username, $balance) = split " ", $line; - next if RevBank::Users::is_special($username); + my ($account, $balance) = split " ", $line; + next if RevBank::Accounts::is_special($account); my $credit = RevBank::Amount->parse_string($balance) or next; $neg += $credit if $credit < 0; diff --git a/plugins/json b/plugins/json index 2ca2c39..7078bae 100644 --- a/plugins/json +++ b/plugins/json @@ -75,17 +75,17 @@ sub hook_retry($class, $plugin, $reason, $abort, @) { _log({ _ => "RETRY", plugin => $plugin, reason => $reason, abort => $abort }); } -sub hook_user_created($class, $username, @) { - _log({ _ => "NEWUSER", account => $username }); +sub hook_account_created($class, $account, @) { + _log({ _ => "NEWUSER", account => $account }); } # NB: stringify transaction_id because future ids might not be numeric. -sub hook_user_balance($class, $user, $old, $delta, $new, $transaction_id, @) { - _log({ _ => "BALANCE", account => $user, old => $old, delta => $delta, new => $new, transaction_id => "$transaction_id" }); +sub hook_account_balance($class, $account, $old, $delta, $new, $transaction_id, @) { + _log({ _ => "BALANCE", account => $account, old => $old, delta => $delta, new => $new, transaction_id => "$transaction_id" }); } -sub hook_checkout($class, $cart, $username, $transaction_id, @) { - _log({ _ => "CHECKOUT", account => $username, transaction_id => "$transaction_id" }); +sub hook_checkout($class, $cart, $account, $transaction_id, @) { + _log({ _ => "CHECKOUT", account => $account, transaction_id => "$transaction_id" }); } diff --git a/plugins/log b/plugins/log index 49e1343..425e711 100644 --- a/plugins/log +++ b/plugins/log @@ -30,18 +30,18 @@ sub hook_retry($class, $plugin, $reason, $abort, @) { _log(RETRY => "[$plugin] $reason"); } -sub hook_user_created($class, $username, @) { - _log(NEWUSER => "$username"); +sub hook_account_created($class, $account, @) { + _log(NEWUSER => "$account"); } -sub hook_user_balance($class, $user, $old, $delta, $new, $transaction_id, @) { +sub hook_account_balance($class, $account, $old, $delta, $new, $transaction_id, @) { my $lost = $delta < 0 ? "lost" : "got"; $delta = $delta->abs; $_ = $_->string("+") for $old, $new; - _log(BALANCE => "$transaction_id $user had $old, $lost $delta, now has $new"); + _log(BALANCE => "$transaction_id $account had $old, $lost $delta, now has $new"); } -sub hook_checkout($class, $cart, $username, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { _log(CHECKOUT => "$transaction_id $_") for map $_->as_loggable, $cart->entries; } diff --git a/plugins/nomoney b/plugins/nomoney index 48c90a1..0ce7842 100644 --- a/plugins/nomoney +++ b/plugins/nomoney @@ -35,7 +35,7 @@ sub _inform($unresolved, $username, $skip_print = 0) { my $broke_users = $entry->attribute('nomoney_users'); for my $account (sort keys %$broke_users) { - my $balance = RevBank::Users::balance($account); + my $balance = RevBank::Accounts::balance($account); my $m = sprintf( "%s have %s", @@ -60,9 +60,9 @@ sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { for my $account (keys %$deltas) { next if $deltas->{$account} > 0; - next if RevBank::Users::is_special($account); + next if RevBank::Accounts::is_special($account); - my $old = $balances{$account} = RevBank::Users::balance($account); + my $old = $balances{$account} = RevBank::Accounts::balance($account); my $new = $old + $deltas->{$account}; next if $new >= 0 or $new > $old; @@ -73,14 +73,14 @@ sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { next if none { $plugin eq $_ } @deny_plugins; my @contra_users = uniqstr sort grep { - not RevBank::Users::is_special($_) + not RevBank::Accounts::is_special($_) and $_ ne $username } map { - $_->{user} + $_->{account} } $entry->contras; next if $allow_multi_user and @contra_users > 1; - next if none { $account eq $_ } $entry->user // $username, @contra_users; + next if none { $account eq $_ } $entry->account // $username, @contra_users; $unresolved->add_entry($entry); } @@ -112,7 +112,7 @@ sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { $entry->attribute('nomoney_users', \%broke_users); for my $account (keys %$trial_deltas) { - next if RevBank::Users::is_special($account); + next if RevBank::Accounts::is_special($account); next if $trial_deltas->{$account} > 0; my $trial_balance = $resolved_balances{$account} + $trial_deltas->{$account}; @@ -144,11 +144,11 @@ sub hook_abort($class, $cart, @) { return; } -sub hook_checkout_done($class, $cart, $username, $transaction_id, @) { +sub hook_checkout_done($class, $cart, $account, $transaction_id, @) { my $n = $unresolved{$cart}->size or return; print "\n"; - _inform($unresolved{$cart}, $username); + _inform($unresolved{$cart}, $account); delete $unresolved{$cart}; my $message = $n == 1 ? "THIS ENTRY WAS IGNORED" : "THESE ENTRIES WERE IGNORED"; diff --git a/plugins/revspace_barcode b/plugins/revspace_barcode index 81d1ccd..02f056e 100644 --- a/plugins/revspace_barcode +++ b/plugins/revspace_barcode @@ -35,7 +35,7 @@ sub data($self, $cart, $input, @) { return ACCEPT; } -sub hook_checkout($class, $cart, $username, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { my @barcodes; for my $entry ($cart->entries('is_barcode')) { push @barcodes, ($entry->attribute('barcode_data')) x $entry->quantity; diff --git a/plugins/revspace_git b/plugins/revspace_git index 327d503..13c1fc4 100644 --- a/plugins/revspace_git +++ b/plugins/revspace_git @@ -1,7 +1,7 @@ #!perl -sub hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @) { - my $msg = "$transaction_id ($username)"; +sub hook_account_balance($class, $account, $old, $delta, $new, $transaction_id, @) { + my $msg = "$transaction_id ($account)"; $msg =~ s/[^\x20-\x7E]//g; $msg =~ s/'//g; diff --git a/plugins/revspace_mollie b/plugins/revspace_mollie index 460261a..bb09cc8 100644 --- a/plugins/revspace_mollie +++ b/plugins/revspace_mollie @@ -57,7 +57,7 @@ sub hook_abort($class, $cart, $reason, @) { for @ids; } -sub hook_checkout($class, $cart, $user, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { # Opportunistic; ignore failures. Can't do anything about it anyway. my @ids = map $_->attribute('mollie_id'), $cart->entries('mollie_id'); diff --git a/plugins/revspace_mqtt b/plugins/revspace_mqtt index 1f0d41c..8a82245 100644 --- a/plugins/revspace_mqtt +++ b/plugins/revspace_mqtt @@ -2,7 +2,7 @@ use Net::MQTT::Simple "mosquitto.space.revspace.nl"; -sub hook_checkout($class, $cart, $user, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { my $filename = "revbank.sales"; my @entries = $cart->entries('product_id') or return; my %already_retained; diff --git a/plugins/revspace_saldo b/plugins/revspace_saldo index 80529e2..b9eb1eb 100644 --- a/plugins/revspace_saldo +++ b/plugins/revspace_saldo @@ -10,16 +10,16 @@ sub _box(@lines) { ); } -sub hook_checkout_done($class, $cart, $user, $transaction_id, @) { - defined $user or return; # hacks like 'undo' don't have an acting user - RevBank::Users::is_hidden($user) and return; +sub hook_checkout_done($class, $cart, $account, $transaction_id, @) { + defined $account or return; # hacks like 'undo' don't have an acting user + RevBank::Accounts::is_hidden($account) and return; - my $balance = RevBank::Users::balance($user) or return; - my $since = RevBank::Users::since($user); + my $balance = RevBank::Accounts::balance($account) or return; + my $since = RevBank::Accounts::since($account); if ($balance < -22.00) { _box( - "Hoi $user,", + "Hoi $account,", "", "Je saldo is $balance en dus lager dan toegestaan. Graag meteen aanvullen!", "Zodra je een positief saldo hebt, mag je weer producten kopen.", @@ -33,7 +33,7 @@ sub hook_checkout_done($class, $cart, $user, $transaction_id, @) { and $1 lt strftime('%Y-%m-%d_%H:%M:%S', localtime(time() - 14 * 86400)) ) { _box( - "Hoi $user,", + "Hoi $account,", "", "Je staat al sinds $1 negatief, dus meer dan 2 weken. Deelnemers", "mogen rood staan, maar niet langdurig. Wil je je saldo even aanvullen?", diff --git a/plugins/statiegeld_tokens b/plugins/statiegeld_tokens index e882294..c3d00c1 100644 --- a/plugins/statiegeld_tokens +++ b/plugins/statiegeld_tokens @@ -125,7 +125,7 @@ sub hook_undo($class, $cart) { # Undo deposit refund: prohibit for my $contra ($entry->contras) { next if $contra->{amount} < 0; - next if List::Util::none { $contra->{user} eq $_ } _addon_accounts; + next if List::Util::none { $contra->{account} eq $_ } _addon_accounts; return ABORT, "Sorry, deposit refunds cannot be undone."; } @@ -151,14 +151,14 @@ sub _handle_undo($cart) { } } -sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { - if ($username eq '-undo') { +sub hook_checkout_prepare($class, $cart, $account, $transaction_id, @) { + if ($account eq '-undo') { _handle_undo($cart); return; } # Read data - my $tokens_by_type = _read->{lc $username}; + my $tokens_by_type = _read->{lc $account}; my $is_new = !defined $tokens_by_type; $tokens_by_type = {} if $is_new; my $time_is_reliable = _time_is_reliable; @@ -235,15 +235,15 @@ sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { # Store data call_hooks( "log_info", - "statiegeld_tokens: ${\scalar @created } created for $username: @created" + "statiegeld_tokens: ${\scalar @created } created for $account: @created" ) if @created; call_hooks( "log_info", - "statiegeld_tokens: ${\scalar @used } used by $username: @used" + "statiegeld_tokens: ${\scalar @used } used by $account: @used" ) if @used; - _write $username, $tokens_by_type, $is_new if $tokens_changed; + _write $account, $tokens_by_type, $is_new if $tokens_changed; return ABORT if %warnings_by_type and not $cart->size; diff --git a/plugins/stock b/plugins/stock index ae64e56..d8ebbeb 100644 --- a/plugins/stock +++ b/plugins/stock @@ -21,7 +21,7 @@ } } -sub hook_checkout($class, $cart, $user, $transaction_id, @) { +sub hook_checkout($class, $cart, $account, $transaction_id, @) { # Hack42 for some reason used the dutch word in their revbank1 hack. my $filename = -e("revbank.voorraad") ? "revbank.voorraad" diff --git a/plugins/tail b/plugins/tail index 35c7f88..db5084a 100644 --- a/plugins/tail +++ b/plugins/tail @@ -15,7 +15,7 @@ sub command :Tab(tail) ($self, $cart, $command, @) { my ($dt, $c, $t_id, $u, $dir, $qty, $amount, undef, $desc) = split " ", $_, 9; $c eq 'CHECKOUT' or next; # real check after expensive split - RevBank::Users::is_hidden($u) and next; + RevBank::Accounts::is_hidden($u) and next; shift @lines if @lines == $n; diff --git a/plugins/undeposit b/plugins/undeposit index 977779c..088d811 100644 --- a/plugins/undeposit +++ b/plugins/undeposit @@ -12,7 +12,7 @@ sub command :Tab(undeposit) ($self, $cart, $command, @) { warn "\n\n\n"; warn "Enter 'abort' to abort.\n"; - @TAB = grep /^[-+]deposit/, RevBank::Users::names + @TAB = grep /^[-+]deposit/, RevBank::Accounts::names or return REJECT, "No contras available."; print "Available contras:\n", map " $_\n", sort(@TAB); diff --git a/plugins/undo b/plugins/undo index 08146e3..fb849d5 100644 --- a/plugins/undo +++ b/plugins/undo @@ -13,11 +13,11 @@ sub command :Tab(undo) ($self, $cart, $command, @) { my @log; for my $line (slurp $filename) { - my ($tid, $user, $delta, $dt) = split " ", $line; + my ($tid, $account, $delta, $dt) = split " ", $line; if (@log and $log[-1]{tid} eq $tid) { - push @{ $log[-1]{deltas} }, [ $user, $delta ]; + push @{ $log[-1]{deltas} }, [ $account, $delta ]; } else { - push @log, { tid => $tid, dt => $dt, deltas => [ [ $user, $delta ] ] }; + push @log, { tid => $tid, dt => $dt, deltas => [ [ $account, $delta ] ] }; } } @@ -49,11 +49,11 @@ sub undo :Tab(&tab) ($self, $cart, $tid, @) { return with_lock { for my $line (slurp $filename) { if ($line =~ /^\Q$tid\E\s/) { - my (undef, $user, $delta) = split " ", $line; + my (undef, $account, $delta) = split " ", $line; $entry ||= $cart->add(0, $description, { undo_transaction_id => $tid }); - $entry->add_contra($user, $delta, "Undo $tid"); + $entry->add_contra($account, $delta, "Undo $tid"); } } @@ -68,8 +68,8 @@ sub undo :Tab(&tab) ($self, $cart, $tid, @) { }; } -sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { - $username eq '-undo' or return; +sub hook_checkout_prepare($class, $cart, $account, $transaction_id, @) { + $account eq '-undo' or return; for my $entry ($cart->entries) { my $undo_tid = $entry->attribute('undo_transaction_id') @@ -85,8 +85,8 @@ sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { } } -sub hook_user_balance($class, $username, $old, $delta, $new, $transaction_id, @) { +sub hook_account_balance($class, $account, $old, $delta, $new, $transaction_id, @) { return if $doing_undo; # don't allow undoing undos - append $filename, join(" ", $transaction_id, $username, -$delta, now()), "\n"; + append $filename, join(" ", $transaction_id, $account, -$delta, now()), "\n"; } diff --git a/plugins/users b/plugins/users index ffab966..6e4fd4b 100644 --- a/plugins/users +++ b/plugins/users @@ -91,7 +91,7 @@ sub _recent($n, $u) { sub balance($self, $u) { _recent(10, $u); call_hooks("user_info", $u); - my $balance = RevBank::Users::balance($u); + my $balance = RevBank::Accounts::balance($u); my $red = $balance->cents < 0 ? "31;" : ""; printf "Balance for $u is \e[%s1m%s\e[0m\n", $red, $balance->string("+"); say "NB: Products/amounts/commands FIRST, username LAST."; diff --git a/plugins/vat b/plugins/vat index 0c2ac3d..dcf1a67 100644 --- a/plugins/vat +++ b/plugins/vat @@ -2,26 +2,26 @@ sub _read_vat { my %vat; for my $line (slurp "revbank.vat") { my ($match, $vataccount, $pct) = split " ", $line; - $vat{lc $match} = { user => $vataccount, pct => $pct }; + $vat{lc $match} = { account => $vataccount, pct => $pct }; } return \%vat; } -sub hook_checkout_prepare($class, $cart, $username, $transaction_id, @) { +sub hook_checkout_prepare($class, $cart, $account, $transaction_id, @) { my $config = _read_vat; for my $entry ($cart->entries) { for my $contra ($entry->contras) { - my $vat = $config->{ lc $contra->{user} } or next; + my $vat = $config->{ lc $contra->{account} } or next; my $amount = RevBank::Amount->new( $contra->{amount}->cents * $vat->{pct} / (100 + $vat->{pct}) ); my $desc = "VAT ($vat->{pct}% * $contra->{amount})"; - my $display = RevBank::Users::is_hidden($contra->{user}) ? undef : $desc; - $entry->add_contra($contra->{user}, -$amount, $desc, $display); - $entry->add_contra($vat->{user}, +$amount, $desc); + my $display = RevBank::Accounts::is_hidden($contra->{account}) ? undef : $desc; + $entry->add_contra($contra->{account}, -$amount, $desc, $display); + $entry->add_contra($vat->{account}, +$amount, $desc); } } } diff --git a/revbank b/revbank index 7bfd19f..23a20c2 100755 --- a/revbank +++ b/revbank @@ -17,7 +17,7 @@ use RevBank::Messages; use RevBank::Cart; use RevBank::Prompt; -our $VERSION = "8.3.1"; +our $VERSION = "9.0.0"; our %HELP1 = ( "abort" => "Abort the current transaction",