Upgrading RevBank

Important notices for system administrators


Upgrade procedure

  1. Stop any running revbank instances, or at least make sure nobody will be using RevBank during the upgrade.
  2. Make a backup of your RevBank data and code repo(s).
  3. Read this file :) to see if you need to change anything. Check your current version and read everything pertaining to newer versions, from oldest to newest (top).
  4. Use git pull --rebase in the right directory. Don't ignore its output, because you may need to manually resolve merge conflicts.
  5. (Re)start revbank. If the old version was still running, use the restart command before issuing any other commands.

The standard deprecation cycle is 2 years. It is recommended that you upgrade RevBank at least once a year.

While you're at it, upgrade the rest of your system too. RevBank currently supports Perl versions down to 5.36 (2022), which is in Debian 12 "bookworm" (oldstable). Once Debian 14 "forkie" is released as stable (expected in 2027) and 13 "trixie" becomes the new oldstable, RevBank will begin to require Perl 5.40 (2024).

(2026-10-20) RevBank 20.0.0

Custom scripts

undo now asks for an explanation, which is logged, and then for a random code shown on screen to confirm. Scripts that undo transactions must also pass both, but they can use SKIP_CONFIRMATION as the code:

revbank -c 'undo 123 "wrong product" SKIP_CONFIRMATION'

(Users can also use that. The confirmation is there to prevent mistakes, not to prevent malice.)

If you're parsing the undo file (why?!), note that the format has changed slightly: undone transactions now stay in the undo file, with the amount written as undone:ID:amount, where ID is the transaction id of the undo.

(2026-10-15) RevBank 19.0.0

RevBank now supports (and requires) UTF-8 everywhere, allowing usernames and product descriptions with accented and non-English letters. (Of course, that still won't work on an old IBM 3151 ASCII terminal :)).

System setup

revbank should now be run under a UTF-8 locale, e.g. export LANG=C.UTF-8. RevBank warns if it's not.

Because RevBank until now only supported ASCII, and UTF-8 is a strict superset, your data files are probably already valid UTF-8. If not, you'll get warnings, and invalid bytes are read as text like \xE9, which ends up in the file when RevBank rewrites it.

Custom plugins

All the Perl strings are now Unicode codepoints, not bytes. If your plugin exchanges text with files, programs or the network itself, encode and decode it as UTF-8; see plugins/tail, plugins/revspace_qr and plugins/revspace_statiegeld_mqtt. That includes arguments to system and exec; use encode_utf8. Printing text that is already encoded as UTF-8 will encode it again.

Comparing account names already had to go through lc (lowercase) for case insensitivity; from now on use RevBank::Accounts::key instead of lc.

Output is now automatically word wrapped to the terminal width (the old assumption was that every terminal was 80 characters wide). If you had long text strings that were manually wrapped, just use one long string. You can use a tab character "\t" to mark where a wrapped line continues. The tab itself is not printed. If too little room is left after the tab, the line breaks there instead, and the rest is indented two spaces more than the line. Existing plugins that printed tabs should have the tabs replaced by spaces. See plugins/help for an example.

(2026-09-27) RevBank 18.0.0

Sorry, changed my mind about a recent addition...

Custom plugins

Enum no longer takes extra arguments. Its overrides are now parameter keys, next to type: move parse, complete and eager there as they are, phrase as rejection_clause, and solo as rejection. A parameter with its own parse is not eager unless it says so, as with Enum's parse before.

The parameter key reject (added in v14.0.0) is deprecated in favour of rejection. It keeps working until 2028-09, but warns.

(2026-09-25) RevBank 17.1.0

This version is a major rewrite of many internals, but should be fully backwards compatible with 17.0.0 (as long as your plugins don't rely on undocumented parts of RevBank).

Custom plugins

This already kinda followed from the v15.0.0 change, but I forgot to announce it explicitly:

The old return "Prompt", \&method form is now deprecated too (which means support will be removed after 2 years).

(2026-09-21) RevBank 17.0.0

Products

There's one change that's pretty visible if you happen to have products with very short barcodes that RevBank would also accept as valid amounts: that now emits warning. But it's just a warning and it keeps working as before: confusingly.

Terminal setup

The prompt now has "shadows" that indicate when there's only one thing left to tab-complete. That doesn't work on some broken terminals, most notably mosh: see #1059, #1276, #1380, and #1404 in their GitHub repository. It looks like mosh isn't going to get fixed: the oldest PR that fixes this has been open for 7 years.

After the upgrade to this version, restart revbank, and type depos. The letters it, that you didn't type, should be shown in a darker color.

On broken terminals that ignore the SGR 2 ANSI escape, the shadowed text looks like normal text, and that can be confusing. Set REVBANK_SHADOW=gray or REVBANK_SHADOW=fake or even REVBANK_SHADOW=off to work around the lack of lower-brightness output.

Custom plugins

Probably affects nobody, but just to be sure:

  • Rewrite uses of hook_deposit_command. I don't think anyone uses that in a custom plugin, but if you do, see plugins/statiegeld for an example.

  • Related: rewrite the use of alternatives (introduced last week) to takeover.

  • Don't depend on RevBank::FileIO::mtime() returning an integer: it now returns fractional seconds on filesystems that support that.

(2026-09-17) RevBank 16.0.0

Custom plugins

Breaking changes to several undocumented features:

  • Don't depend on $cart being the same across transactions, because it no longer is: after abort or a successful checkout, a new one is instantiated.

  • Don't use $cart->empty anymore. This was used for abort and after a successful checkout and probably never made sense in custom plugins anyway. If you need to start an empty transaction, abort the current one.

  • Don't depend on the class name of $cart, including stringification, for example by using it as a hash key. The cart is now sealed when the transaction is being finalized, to help prevent accidental changes in that stage. For this, the object is reblessed, which means the class name changes.

  • Change $cart->{...} reads and writes to the new $cart->attribute(...) interface. It works like the attribute method that already existed for cart entries. The attributes are rolled back when payment fails and the user can retry.

  • Change $entry->{...} reads and writes to the accessor methods such as $entry->amount and $entry->description for the main keys and $entry->attribute(...) for custom attributes. This should already have been done like that, but even some core plugins misbehaved.

There was also a change to how tab completion callbacks work, but those were only introduced 5 days ago so I don't think anyone will have used it in a custom plugin.

(2026-09-12) RevBank 15.0.0

Custom plugins

Rewrite every sub command { ... }

There's a new way to implement commands in a plugin, and the old one is now deprecated. That means you have until 2028-09 to update custom plugins. But it's recommended to do it sooner because the new format has some nice advantages.

All plugins shipped with RevBank have been converted, so there are plenty of examples to read: see plugins/products and plugins/mollie for command_MATCH, plugins/adduser_note for hook_command.

The new way allows for even better context-aware tab completion and hints on the main prompt, by separating information gathering from the actual execution.

The command_keyword subs must be "pure", which means: free of side-effects such as updating the $cart, printing anything, executing system commands, etcetera. You can, however, return a sub which does all those things. See the Purity section in RevBank::Plugins for details.

Here's a rough guideline for converting existing plugins:

  1. Separate your different commands into different subs, one for each command.
  2. For each command, separate the information gathering from the execution: return a declaration of what should come next. If it's a simple command with no parameters, just define run. If you do have parameters, define params too. Returning ACCEPT should only ever take place in run.
  3. For the more complicated commands, see Declarative commands in RevBank::Plugins (perldoc lib/RevBank/Plugins.md).

If a command follows a pattern rather than a fixed word (a product id, a username, a voucher code, etc.), name the sub command_MATCH, which will be called for all command input regardless of what the user typed. Like the old command, returning NEXT means the plugin is not interested in the command.

Example of a plugin with a command foo that takes one username, and a command bar that just executes immediately:

# Old:

sub command :Tab(foo,bar) ($self, $cart, $command, @) {
    if ($command eq 'foo') {
        return "Which user?", \&user_answer;
    } elsif ($command eq 'bar') {
        # do bar things
        return ACCEPT;
    }
    return NEXT;
}

sub user_answer :Tab(USERS) ($self, $cart, $input, @) {
    my $user = parse_user($input);
    # do foo things, with $user as the username
    return ACCEPT;
}

# New:

sub command_foo($self, $cart, $command, @) {
    return params => [
        {
            name => "user",
            type => User,
            prompt => "Which user?",
        }
    ],
    run => sub($args) {
        # do foo things with $args->{user} as the username.
        return ACCEPT;
    };
}

sub command_bar($self, $cart, $command, @) {
    return run => sub {
        # do bar things
        return ACCEPT;
    };
}

For a slightly longer example, see plugins/give.

For a very complicated example, see plugins/deposit.

New: hook_command

It is called in the execution phase just before the command methods, and does allow impure things such as manipulating the cart or even rewriting the input word itself. This was needed to rewrite some of the more arcane things to the new command methods.

(2026-09-10) RevBank 14.0.0

(Merged into the v15.0.0 section.)

(2026-09-08) RevBank 13.0.0

Changes to hook_checkout_prepare: it now runs without the global lock, and no longer gets a $transaction_id. That makes it possible to do slow things (like interactive card payments) without blocking other revbank instances.

If you wrote your own plugin with a hook_checkout_prepare, remove $transaction_id from the signature (it will get undef for the forseeable future).

There's a new method, $cart->defer that can be given a function that will be executed when the lock is held. It does get the transaction ID.

In short, in custom plugins, change this:

sub hook_checkout_prepare($class, $cart, $account, $transaction_id, @) {
  prepare();
  write_file($transaction_id);
}

to this:

sub hook_checkout_prepare($class, $cart, $account, @) {
  prepare();
  $cart->defer(sub ($transaction_id) {
    write_file($transaction_id);
  });
}

The change also means that there can now be more race conditions of the TOCTOU kind. If this is relevant for your application, you may need to check again just before using, or use a delta instead of a new version.

(2026-02-22) RevBank 12.3.0

The length of new usernames is now limited to 16 characters by default. See plugins/config.example if you want a different number.

This a cosmetic change, to ensure sufficient room for other information on 80x24 terminals without having to truncate names. Existing accounts will continue to work.

(2026-03-02) RevBank 12.0.0

Removed: old products syntax

As announced in 2024, support for the deprecated old format of the products list has been removed. See the upgrade notice for version 9.0.0 for details.

Semicolons instead of commas in product files

Aliases in the product file are now ;-separated instead of ,-separated.

Updating the existing products file

Assuming you don't have any quoted or escaped product IDs, the following can automatically upgrade the file for you:

perl -i.bak -ne'my @f = split /( )/,$_,2; $f[0] =~ s/,/;/g; print @f' \
    ~/.revbank/products

If you do have quoted or escaped product IDs, rewrite those lines yourself.

Compatibility

For the next 2 years, RevBank will retain compatibility with the comma separated product IDs. (Although it will emit a warning that points to these instructions.)

Because of this, you still can't use a comma in a product ID without the following trick: add a ; in front of the Product ID to force the new parsing.

# These will change meaning in 2028:
primary,alias      0.01   "Old style: 'primary' and 'alias'"
"primary,alias"    0.01   "Old style: 'primary' and 'alias' (same as previous)"

# New syntax:
primary;alias      0.42   "New style: 'primary' and 'alias'"
"primary";"alias"  0.42   "New style: 'primary' and 'alias' (same as previous)"

# Forcing new parsing to allow literal comma in product ID:
;has,,commas,      1.23   "Example with a single ID that has commas"
;"has,,commas,"    1.23   "Example with a single ID that has commas (same as previous)"

Background info

Semicolons make more sense as a separator, because (unlike commas) they are already special in the main command line, so the user can't enter them easily anyway.

Allowing commas in product IDs has been requested several times, but the comma was used for aliasing. Another requested feature was to allow whitespace around the separator for those who mentally interpret the file as having fixed-width columns instead of simple whitespace separation.

(2026-01-05) RevBank 11.1.0

A bug fix involves a small breaking change, which is unlikely to affect anyone: a feature was removed, which was probably not actually used by anyone, because it wasn't implemented correctly.

To check whether you're affected by this change, grep .revbank/accounts for -expenses/reimbursed. If that account exists and you wish to keep using it, keep reading.

The bug was that the intended hard-coded contra account -expenses/reimbursed was only used if the deposit_methods plugin had zero follow-up prompts for the reimburse method. It was intended to be used with all reimbursements.

This version introduces a way to define a custom contra account for a deposit method. For example, adding contra => "-expenses/reimbursed" to the definition of the reimburse method in your deposit_methods plugin will basically do the same as the removed feature, but regardless of whether any follow-up prompts are defined.

(2025-10-06) RevBank 11.0.0

Support for the deprecated timestamp-based transaction IDs was removed.

This means that if you wrote LEGACY to the nextid file and forgot to inform me that you want to keep this feature, your next transaction will have the ID LEGACY, followed by LEGACZ, LEGADA, LEGADB, and so on.

(2025-09-04) RevBank 10.6.1

Minimum Perl version bumped to 5.36 (the version in Debian oldstable).

Plugins now run under use v5.36; if they don't specify a different version of Perl. With modern Perl code this shouldn't break anything, but if a custom plugin can no longer be loaded, these are some probably causes:

  • Indirect object method syntax is now disabled. That means the looks-like-other-languages syntax new ExampleClass(...) no longer works. Rewrite as ExampleClass->new(...).

  • Smart match is now disabled. That means the keywords given and when are no longer available. Rewrite as normal if/elsif/else blocks.

  • Fake multidimensional arrays are now disabled. That means that the weird $foo{$i, $j} syntax no longer works. Rewrite as $foo[$i][$j], which uses nested arrays.

Additionally, the plugin deprecated_raw has been removed from the repository. If you were still using that, remove it from ~/revbank/plugins.

(2025-05-06) RevBank 10.2.0, 10.3.0

No breaking change, but a change that several RevBank sysadmins have requested over the years.

It is now possible to load plugins from other directories. It is suggested that you put your custom plugins in a separate directory (hint: git init :)), and refer to them by path in ~/revbank/plugins.

Some variables are now exposed as global variables that can be overridden in a custom plugin, see plugins/config.example.

(2025-05-05) RevBank 10.0.0

Major breaking change!

Instead of dumping many files in the working directory, RevBank now uses a (configurable) data directory. This is ~/.revbank by default but it can be overridden with the environment variable REVBANK_DATADIR or --datadir on the command line. (If both are specified, the latter overrules the former.)

This change makes it easier to make backups of just the data files, and allows running revbank from any working directory.

Create a data directory

Create a directory named .revbank in the home directory of the user account that runs revbank:

mkdir ~/.revbank

If this already exists, you've probably executed revbank before reading these instructions. Check that the files are just the example files that RevBank put there, and just delete them.

Rename data files

To migrate your existing files, execute from the directory that has them:

mv revbank.accounts   ~/.revbank/accounts
mv revbank.market     ~/.revbank/market
mv revbank.plugins    ~/.revbank/plugins
mv revbank.products   ~/.revbank/products
mv revbank.sales      ~/.revbank/revspace_mqtt
mv revbank.statiegeld ~/.revbank/statiegeld
mv revbank.stock      ~/.revbank/stock
mv revbank.vat        ~/.revbank/vat
mv revbank.voorraad   ~/.revbank/stock  # overwrites .stock if both exist
mv revbank.warnings   ~/.revbank/warnings
mv .revbank.nextid    ~/.revbank/nextid
mv .revbank.oepl      ~/.revbank/oepl
mv .revbank.undo      ~/.revbank/undo
mv .revbank.log       ~/.revbank/log
rm .revbank.global-lock

Some of these only exist if you use certain plugins, so don't worry about error messages saying the files don't exist.

Note, if you've customized/forked the revspace_mqtt plugin, that its filename has changed to match the name of the plugin.

If the log file was append-only

If you get Operation not permitted on .revbank.log and/or .revbank.undo, you probably have protected them with chattr +a to only allow appending. Use chattr -a (as root!) to remove the protection, then move the file, then +a it again.

If you used git before

Many RevBank installations had the data files in git, and this change is there to make that easier and to disentangle the data files from the code repository.

Unfortunately, moving the existing git repo can be tough and takes some real git experience to do correctly, because everyone's repo probably looks a bit different. But in general: git mv revbank.foo foo instead of mv revbank.foo ~/.revbank/foo, then move both .git and the actual revbank files to the new directory.

It may be easier to start over, using the new git plugin:

Use git

Many spaces had their own plugin for committing changes to git. RevBank now comes with a standard plugin, git, that does this in a more generic way. You may want to enable it by adding it to ~/.revbank/plugins.

Note: this plugin does not git push to a remote repository. You could do that with a cron job or a systemd timer, which also spares the RevBank end user from long delays if the network is borked.

Note: this plugin creates a .gitignore file to ignore the log files because it is generally not advised to keep log files in a revision control system. If you do want to keep the log files in git, edit ~/.revbank/.gitignore after it has been created. It's probably better to use a real backup solution, not (just) git.

Update external things that use the RevBank data files

Don't forget to update any backup configuration, scripts, and other things that use the RevBank data files.

Update custom plugins

The functions slurp and spurt will now prefix the path of the datadir to the given filename. Previously they worked from the current working directory.

If you're reading or writing any of the files listed above, those needs to be changed.

(2025-04-10) RevBank 9.0.0

There are no breaking changes in this release, but the old names mentioned below are now deprecated. All custom plugins that use these identifiers need to be updated eventually.

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. (This is technically a breaking change, but changing that value from a plugin would probably break things anyway.)

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!

Breaking change:

This is very unlikely to affect anyone, but still: percent addons (like discounts) applied by read_products now have the calculated price in ->{price}, and the percent amount was moved to ->{percent}, which was previously just a boolean.

This change has had no deprecation cycle because I don't think anyone would be using this in custom code. But if you did use this feature in a custom plugin (wow, I really want to know all about it!), just change price to percent where appropriate.

Non-breaking changes:

  • RevBank::Plugins::products::read_products was moved to RevBank::Products::read_products, but the old symbol still works.

  • read_products gained some additional features, such as price tag calculations. Top-level products now have ->{tag_price}, ->{hidden_fees}, and ->{total_price} in addition to the existing base price which is still in ->{price}.

  • Because read_products is now in a module, you can use RevBank::Products; in your own scripts so you don't have to write your own parser for revbank.products anymore. (Don't forget to use lib "path/to/lib"; first!)

The calculated tag prices are not displayed anywhere in RevBank, but meant for an upcoming feature which is to generate images for electronic price tags. To exclude addon prices from the price tag (as is customary with statiegeld/pfand/deposits), add the new #OPAQUE hashtag to the respective addon lines in revbank.products.

Deprecation announcement

  • Support for the old file format for revbank.products will be removed in
  • The new format was introduced in 6.0.0 in January 2024, but the old format still works (and it gives a lot of warnings if you use it). See below for how to update your products file.

  • The plugin deprecated_raw will be removed after February 2025. This plugin warns tells users to use withdraw or unlisted instead of a raw amount, after support for that was dropped in 3.3 in June 2022.

(2024-11-17) RevBank 7.1.0

The new plugin nomoney is enabled by default. For rationale, see https://forum.revspace.nl/t/inkoopacties-via-revbank/469.

Whether this constitutes a breaking change is debatable, and it wasn't added to this file until 2025-03-06. It's a new feature, but the feature is to disallow some transactions which used to be allowed. (Specifically, it denies transactions if the user has insufficient balance; by default only for give/take/withdraw, but the list of affected plugins can be customized.)

(2024-10-18) RevBank 7.0.0

Support for unbalanced entries has been removed, ensuring a pure double-entry bookkeeping system. Grep your log for the string UNBALANCED if you're not sure that all your custom plugins are already well-behaved. Note that since unbalanced transactions are no longer supported, transactions from before that change can't be reverted with undo.

There are no other changes in this version.

Since all transactions are now balanced, the sum of all the balances is revbank.accounts will remain fixed forever. It is recommended to make that sum equal to 0.00 (only once) by adding a dummy account which acts a retroactive opening balance:

perl -Ilib -MRevBank::Amount -lane'$sum += RevBank::Amount->parse_string($F[1])
}{ printf "-deposits/balance %s\n", -$sum if $sum;' revbank.accounts >> revbank.accounts

From that point forward, the sum of all the values in the second column of the revbank.accounts file should forever be 0.00; if it's not, either someone tampered with the file or there is data corruption, and the cause should be investigated and corrected.

perl -Ilib -MRevBank::Amount -lane'$sum += RevBank::Amount->parse_string($F[1])
}{ print $sum' revbank.accounts

(2024-01-20) RevBank 6.0.0

Note that the changes to revbank.products do NOT apply to revbank.market and other files.

Update your revbank.products file

TL;DR: Product descriptions now need "quotes" around them.

This version comes with breaking changes to the revbank.products syntax, to expand the capabilities of the file in a more future-proof way. Bitlair (Hackerspace Amersfoort) has requested a way to add metadata to products for automation, which together with recent other additions to the format, made clear a more structured approach was needed.

The line format for the products file is now like the input format of the command line interface. This means that if product descriptions contain spaces, as they typically do, quotes are needed around them. You can pick between "double" and 'single' quotes. Any backslashes and quotes within the same kind of quotes need escaping by adding a \ in front, e.g. \" and \\.

# Old format:
product_id    0.42    Can't think of a good description +addon1 +addon2

# New format, recommended style:
product_id    0.42    "Can't think of a good description" +addon1 +addon2

# Automatically generated? You may wish to quote all fields:
"product_id" "0.42" "Can't think of a good description" "+addon1" "+addon2"

# Escaping also works:
product_id 0.42 Can\'t\ think\ of\ a\ good\ description +addon1 +addon2

To convert your revbank.products file to the recommended style automatically, you could use:

# The following is one command. It was obviously not optimized for readability :)

perl -i.backupv6 -ple'unless (/^\s*#/ or /^\s*$/) {
  my ($pre, $desc) = /(^\s*\S+\s+\S+\s*)(.*)/; $pre .= " " if $pre !~ /\s$/;
  my @a; unshift @a, $1 while $desc =~ s/\s\+(\S+)$//;
  $desc =~ s/([\"\\])/\\$1/g; $_ = "$pre\"$desc\"";
  for my $a (@a) { $_ .= " +$a" }
}' revbank.products

Note that this will leave commented lines unchanged! If those contain disabled products, you'll have to add the quotes yourself.

New feature: hashtags in revbank.products

After the description field, you can add hashtag fields. These begin with # and may take the form of a lone #hashtag, or they may be used as a #key=value pair. The hashtags can be read by plugins. Out of the box, they currently do nothing.

8711327538481  0.80  "Ola Liuk"   #ah=wi162664 #q=8
8712100340666  0.45  "Ola Raket"  #ah=wi209562 #q=12
5000112659184,5000112658873  0.95  "Coca-Cola Cola Zero Sugar (33 cl)" #sligro +sb

# equivalent:
"8711327538481" "0.80" "Ola Liuk" "#ah=wi162664" "#q=8"

See https://git.bitlair.nl/bitlair/revbank-inflatinator for a possible use of adding metadata.

(2023-12-26) RevBank 5.0.0

This version comes with breaking changes to the command line syntax, to shield overconfident users of the interface for advanced users from several classes of common mistakes, and to add support for quoted and escaped strings to this interface.

Basically, you can now use ; to separate multiple commands on a single line of input, and in some cases this is mandatory.

Limited set of characters allowed in usernames and product IDs

Historically, RevBank has allowed almost every character as a valid character, because it wasn't known if these would show up in barcodes. In more than 13 years of real world use, though, it seems that barcodes and usernames with "special" characters are incredibly uncommon.

Since ' " \ ; now have special meanings, they are no longer supported in product IDs. In theory, they could be quoted or escaped, but barcode scanners don't know that. Existing users with those characters in their names can continue to use their accounts by quoting or escaping them.

New usernames must now only contain the characters from the set A-Z a-z 0-9 _ - + / ^ * [] {} and the first character must not be any of - + / ^ *.

Update scripts that run revbank commands

When providing multiple commands on a single line, RevBank now requires a separating ; after commands that finalize transactions, and after commands that take arguments.

End-users are guided interactively to deal with the change, but automated commands require changing. Specifically, add a ; between a multi-word command and the final username (e.g. give *lasercutter 10; xyzzy) and in between transactions.

Update your custom plugins

  • The undocumented feature ROLLBACK_UNDO is gone. Use return ABORT in a function called hook_undo instead.
  • Plugins are now evaluated with an implicit use v5.32; which enables many new Perl features and disables some old ones. Specifically, the old-style "indirect object notation" is disabled, which means that new Foo(...) should be rewritten as Foo->new(...).
  • $cart->checkout now throws an exception if there is unprocessed input in the queue (the user can use ; if it was intentional). There were always reasons a checkout could fail, but now it is much more likely. Things that should only happen if the checkout succeeds, should be put after the call, or in a hook.

(2023-11-05) RevBank 4.2.0

Accounts that begin with * are now special: like hidden accounts, they do not count towards the grand total, but unlike hidden accouns, they can be used as normal user accounts too.

The intended application is for liabilities accounts that are also used directly for revenues and expenses.

They can be used with or without the * prefix, e.g. the account *lasercutter can also be used as lasercutter. Such accounts cannot be created from within the software: to create a user-accessible special account, you need to edit revbank.accounts manually.

When upgrading, check that no accounts beginning with * already exist.

(2023-09-20) RevBank 4.0.0

You must pick a transaction ID style

Transaction IDs are now sequential for better auditability. In previous versions, they were timestamps (unix time minus 1.3e9).

Because of this change, you must restart every running RevBank instance or else the transaction IDs will no longer be monotonic between processes, which would be bad.

You should choose which transaction IDs you want, and write your choice to a file called .revbank.nextid.

Option 1: continue with large IDs but increment by 1 from now on

If you don't write a .revbank.nextid file, RevBank will create one for you, but you might not like it. It will generate one more timestamp based ID and then increment that for subsequent transactions. This has the advantage of not having the one-time break of monotonicity, but you will be stuck with the long IDs and they will no longer convey time information.

Option 2: beginning a new sequence

Anything that works with Perl's ++ operator will work, and that gives a few options. If you want to start over with transaction ID 1, write that to the file:

echo 1 > .revbank.nextid

You can also use padding zeroes if you like. They will safely overflow to use an extra digit after all-nines is reached:

echo 00001 > .revbank.nextid

(You can also use alphanumeric IDs, but I'm not sure if you should.)

Or, if you still have all the logs from since you started using RevBank, you can pretend RevBank has always had simple incremental transaction IDs and use the number of distinct transaction IDs from the log file as the basis for the next ID:

# This is my personal preference

perl -lane'BEGIN { $max = time() - 1.3e9 }
    /^\d+$/ and $_ > 0 and $_ < $max and $x{$_}++ for @F[1, 2];
    }{ print 1 + keys %x' .revbank.log > .revbank.nextid

# Note: use multiple filenames (e.g. .revbank.log*) if you rotate log files
# (like when you have yearly logs).

This is safe because the timestamp based IDs were huge and are unlikely to overlap at least the next few decades.

~~Option 3: keeping the legacy transaction ID scheme (for now)~~

~~Finally, for those who really don't want to change the scheme now, the old system can be retained by writing the special-cased value LEGACY. This feature will be supported at least until 2024-01-01, but might be removed after if nobody tries to convince me otherwise.~~

(Removed in 11.0.0)

Update revbank.plugins

There are a few new plugins that you may wish to enable. Some have been around longer than RevBank 3.9, but haven't been mentioned in UPGRADING.md before.

vat

Automatically calculate and set aside VAT ("BTW" in Dutch) on generated revenue. You will probably not need this. Before enabling this plugin, read the documentation in plugins/vat.md first.

regex_gtin

To support GS1 Digital Links and other GS1 barcodes. The DL are a new way for QR codes that contain product IDs and other metadata while also being usable for promotional stuff. At least one popular brand of soft drinks is already using them. There's a huge standard that describes these codes, but basically, they're URLs with /01/ and a 14-digit product ID in them. Enabling this plugin is probably useful and harmless; add it to revbank.plugins after plugins that deal with product IDs like products and market.

regex_angel

Replaces custom SHA2017/MCH2022 angel badge hacks. Add after users in revbank.plugins after removing your custom plugin for angel- barcodes.

adduser_note

Add before adduser in revbank.plugins. This will inform new users that RevBank is insecure by design and what implications that can have. Enabling this plugin is recommended.

statiegeld and statiegeld_tokens

Charge and refund container deposit return ("statiegeld" in Dutch). Read the documentation in plugins/statiegeld.md and plugins/statiegeld_tokens.md for instructions.

cash_drawer

If you have an electronic cash drawer, copy or change this plugin and add code to trigger it whenever something is done that involves cash.

Deprecation note

RevBank has supported "doubly entry bookkeeping" since version 3.4 last year. For backwards compatibility with custom plugins, support for unbalanced transactions was retained.

Support for unbalanced transactions will be removed after 2024-06-10, after a period of 2 years after the introduction of balanced transactions. If you're using custom plugins, grep your log file for the text "UNBALANCED ENTRY" to see if changes are needed.

(2023-08-21) RevBank 3.9

A tiny change that could break things: the shebang was changed from #!/usr/bin/perl to the more modern #!/usr/bin/env perl.

In the unlikely event that your system has multiple perl executables in the search path of PATH, this change could mean that revbank now uses a different perl, in which case you may have to reinstall the required Perl libraries.

Background: NixOS doesn't follow the previously uni(x)versal convention that, when Perl is available, an executable exists at /usr/bin/perl. The only stable paths that NixOS provides for shebangs are #!/bin/sh or #!/usr/bin/env. There were always pros and cons to switching the shebang to env (e.g. for use with perlbrew), but learning about Nix has tipped the scales for me. (The performance penalty is not relevant for RevBank.)

(2023-02-12) RevBank 3.8

Update your revbank.plugins

Deduplication is moved from individual plugins to a plugin that does that. If you want to keep deduplication of cart items, and you probably do want that, add deduplicate to revbank.plugins just below repeat.

The deprecation warning was moved from the withdraw plugin to a new plugin called deprecated_raw. If you're upgrading from an older versions and some of your users have been around since before the withdraw/unlisted split, you may want to keep the deprecation warning. But for new RevBank installations it does not make sense. To keep providing these warnings to users that enter raw amounts, add deprecated_raw to the very end of revbank.plugins.

(2022-12-25) RevBank 3.6

Update your revbank.plugins

The edit command is now in its own plugin, so that it can be disabled (this has been requested several times). To keep the ability to edit the products list from within RevBank, add edit to revbank.plugins.

Check your revbank.products

Added 2024-01-20 v6.0.0: if you're upgrading to v6.0.0 from a version before v3.6, instead of following these instructions, you can just add quotes to the descriptions (when using the perl oneliner from the v6.0.0 upgrade instructions, check if any +something that got placed outside of the quotes should have been within the quotes.)

~~There's new syntax for revbank.products: addons. Check that your lines don't have +foo at the end, where foo can be anything.~~

~~Also check that you don't have any product ids that start with +; those can no longer be entered as this syntax now has special semantics.~~

~~So these don't work as before:~~

example_id      1.00  Example product +something
+something      1.00  Product id that starts with plus
example,+alias  1.00  Alias that starts with plus

~~These will keep working as they were:~~

example_id1     1.00  Example product+something
example_id2     1.00  Example product + something
more_stuff      1.00  Example product with +something but not at the end
bbq             1.00  3+ pieces of meat

New features in products plugin

There are several new features that you may wish to take advantage of. By combining the new features, powerful things can be done that previously required custom plugins.

The syntax for revbank.products has become complex. Please refer to the new documentation in products.md for details.

Negative prices (add money to account)

Support for non-positive prices was requested several times over the years and has now finally been implemented.

It's now possible to have a product with a negative amount, which when "bought" will cause the user to receive money instead of spending it.

Product addons

It is now possible to add products to products, which is done by specifying +foo at the end of a product description, where foo is the id of another product. This can be used for surcharges and discounts, or for bundles of products that can also be bought individually.

Explicit contra accounts

By default, products sold via the products plugin, are accounted on the +sales/products contra account. This can now be overridden by specifying @accountname after the price in revbank.products. For example, 1.00@+sales/products/specificcategory. While this will mess up your tidy columns, you may be able to get rid of a bunch of custom plugins now.

When the specified contra account is a regular account (does not start with + or -), this works similar to the market plugin, but without any commission for the organization.

Pfand plugin: gone

The pfand plugin, that was originally written as a proof-of-concept demo, has been removed without deprecation cycle. To my knowledge, nobody uses this plugin. If you did use it, just grab the old version from git. Please let me know about your usecase!

The introduction of beverage container deposits in The Netherlands has triggered reevaluation, and several things about that plugin were wrong, including the condescending comments that bottle deposits for small bottles would be crazy or wouldn't make sense in a self-service environment. RevBank was too limited to support it properly, but I think current RevBank fulfills all requirements for making a better, proper pfand plugin.

Perl warnings are now enabled for plugins

If you get Perl warnings from a plugin, and don't want to fix the issues with the code (or disagree with the warning), just add "no warnings;" to the top of the plugin file. However, the warnings are often indicative of suboptimal code that is ground for improvement!

Most warnings will be about unitialized (undefined) values. Some guidance for Perl newbies: you can test whether something is defined with if (defined($foo)) { ... }, or provide a default value with $foo // "example default value".

(2022-08-30) RevBank 3.5

RevBank now has a simple built-in text editor for products and market; rationale in lib/RevBank/TextArea.md.

This comes with a new dependency, the perl module Curses::UI (debian: libcurses-ui-perl).

(2022-06-11) RevBank 3.4

RevBank now has built-in hidden accounts and balanced transactions (double-entry bookkeeping). These accounts will be made automatically, and hidden from the user interface.

Update external scripts

If you have scripts that parse .revbank.log or revbank.products, you may want to ignore all accounts that start with - or +.

User account names that are now invalid

In the hopefully very unlikely event that you have existing user accounts that start with - or +, those will have to be renamed manually, as such accounts are no longer accessible.

Updating custom plugins (optional for now)

For your custom plugins, you may want to add ->add_contra calls to every $cart->add call that does not already have them. Unbalanced transactions will probably be deprecated in a future version.

New feature: cashbox tracking

The new cash plugin will display messages about how much the cash box should hold, whenever someone withdraws or does a cash deposit. For that to make sense, this requires the deposit_methods plugin to be enabled, and to have a "cash" deposit method.

When adding the cash plugin in revbank.plugins, make sure it is listed before stock if you have that one too. And you probably want to enable the skim plugin too, which introduces the (hidden) commands skim and unskim which can be used to keep the cash box data synchronised when someone (probably a board member) skims it.

(2022-06-04) RevBank 3.3

Raw amounts without a command are no longer supported. There was already an explicit command for unlisted products, unlisted, and for withdrawals there is now the new command withdraw. An explanatory message guides users who use the old style towards the new commands.

This change makes it possible for treasurers to more accurately deduce the intention of a revbank transaction.

When upgrading, make sure the unlisted plugin is installed in revbank.plugins. Without it, the instruction text presented when someone enters an amount is wrong and the functionality for paying for unlisted products is lost.

(2021-12-02) RevBank 3.2

Update your custom plugins!

Test your custom plugins. If they don't emit warnings about floating point numbers, or if you don't care about warnings, then no changes are required.

RevBank no longer uses floating point numbers for amounts. Instead, there are now RevBank::Amount objects, which internally store an integer number of cents, but externally stringify to formatted numbers with 2 decimal places.

To create such an object, use parse_amount as per usual.

Formatting no longer requires sprintf %.2f, just use %s instead.

Using an amount as a floating point number will now emit warnings in some cases, to alert you to the fact that this may result in rounding errors. To convert an amount to a floating point number without a warning, use $amount->float. To convert a floating point number to an amount without a warning, use RevBank::Amount->new_from_float($float).

Most hard-coded uses of floats are safe enough and transparently supported through overloaded operators, but if there are more than 2 decimal places, the operation will be disallowed.

(2019-11-05) RevBank 3

The following features were removed:

  • plugins nyan and game

    Please remove these from your revbank.plugins configuration file.

  • creating new accounts with deposit

    Use adduser instead.

Update your custom plugins!

Method $cart->is_multi_user

Method has been removed.

Method $cart->delete($user, $index)

Method has been removed.

Delete a specific entry, as returned by $cart->entries, instead.

Hooks add and added

Use add_entry and added_entry instead, which gets a RevBank::Cart::Entry object, instead.

Note that the new "entries", unlike old "items", can have a quantity other than 1.

Method $cart->add(undef, ...)

Method $cart->add($user, ...)

The add method now always creates an entry from the perspective of the current user, and returns a RevBank::Cart::Entry object to which "contras" can be added with add_contra. The contras can be used for counteracting a value with an operation on another account.

To upgrade a plugin that does a single add with undef as the first argument, simply remove the undef,. When multiple items were added that belong together, consider using add_contra for the subsequent lines; see the take and give plugins for examples.

Method $cart->select_items

Use entries instead, which takes the same kind of argument. Note that entries work slightly differently: they can have a quantity and attached contra entries. Attributes are now accessed through the has_attribute and attribute methods, instead of directly manipulating the hash.