diff --git a/UPGRADING.md b/UPGRADING.md index 30d7ed8..6cfa6dc 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,3 +1,20 @@ +# (2022-06-11) RevBank 3.4 + +RevBank now has built-in hidden accounts and balanced transactions. These +accounts will be made automatically, and hidden from the user interface. Apart +from that, they function as normal accounts (for now). + +If you have scripts that parse `.revbank.log` or `revbank.products`, you may +want to ignore all accounts that start with `-` or `+`. + +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. + +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. + # (2022-06-04) RevBank 3.3 Raw amounts without a command are no longer supported. There was already an diff --git a/lib/RevBank/Cart.pm b/lib/RevBank/Cart.pm index 2ff8d9e..e22bcd8 100644 --- a/lib/RevBank/Cart.pm +++ b/lib/RevBank/Cart.pm @@ -66,11 +66,7 @@ sub checkout($self, $user) { return; } - if ($user =~ /^[-+]/) { - # Hidden internal accounts - my $canonical = RevBank::Users::parse_user($user); - $user = $canonical // RevBank::Users::create($user); - } + $user = RevBank::Users::assert_user($user); my $entries = $self->{entries}; diff --git a/lib/RevBank/Cart/Entry.pm b/lib/RevBank/Cart/Entry.pm index c4c808a..e927e13 100644 --- a/lib/RevBank/Cart/Entry.pm +++ b/lib/RevBank/Cart/Entry.pm @@ -27,6 +27,7 @@ sub new($class, $amount, $description, $attributes = {}) { sub add_contra($self, $user, $amount, $description) { $amount = RevBank::Amount->parse_string($amount) if not ref $amount; + $user = RevBank::Users::assert_user($user); $description =~ s/\$you/$self->{user}/g if defined $self->{user}; @@ -84,7 +85,7 @@ sub as_printable($self) { push @s, sprintf "%8s %s", $self->{amount}->string_flipped, $self->{description}; for my $c ($self->contras) { - next if RevBank::Users::is_hidden($c->{user}); + next if RevBank::Users::is_hidden($c->{user}) and not $ENV{REVBANK_DEBUG}; push @s, sprintf( "%11s %s %s", diff --git a/lib/RevBank/Messages.pm b/lib/RevBank/Messages.pm index af39c49..b716932 100644 --- a/lib/RevBank/Messages.pm +++ b/lib/RevBank/Messages.pm @@ -55,7 +55,7 @@ sub hook_reject($class, $plugin, $reason, $abort, @) { } sub hook_user_balance($class, $username, $old, $delta, $new, @) { - return if hidden $username; + return if hidden $username and not $ENV{REVBANK_DEBUG}; my $sign = $delta->cents >= 0 ? '+' : '-'; my $rood = $new->cents < 0 ? '31;' : ''; @@ -67,7 +67,7 @@ sub hook_user_balance($class, $username, $old, $delta, $new, @) { } sub hook_user_created($class, $username, @) { - return if hidden $username; + return if hidden $username and not $ENV{REVBANK_DEBUG}; say "New account '$username' created."; } diff --git a/lib/RevBank/Users.pm b/lib/RevBank/Users.pm index 35fe23b..e08ab2b 100644 --- a/lib/RevBank/Users.pm +++ b/lib/RevBank/Users.pm @@ -7,6 +7,7 @@ no warnings qw(experimental::signatures); use RevBank::Global; use RevBank::Plugins; +use Carp (); my $filename = "revbank.accounts"; @@ -77,16 +78,30 @@ sub update($username, $delta, $transaction_id) { ); } -sub parse_user($username) { - my $users = _read(); - return undef if not exists $users->{ lc $username }; - return $users->{ lc $username }->[0]; -} - sub is_hidden($username) { return $username =~ /^[-+]/; } +sub parse_user($username) { + return undef if is_hidden($username); + + my $users = _read(); + return exists $users->{ lc $username } + ? $users->{ lc $username }->[0] + : undef; +} + +sub assert_user($username) { + my $users = _read(); + + return exists $users->{ lc $username } + ? $users->{ lc $username }->[0] + : (is_hidden($username) + ? create($username) + : Carp::croak("Account '$username' does not exist") + ); +} + 1; diff --git a/plugins/deposit b/plugins/deposit index 6ac93cd..0de950d 100644 --- a/plugins/deposit +++ b/plugins/deposit @@ -19,7 +19,10 @@ sub amount :Tab(13.37,42) ($self, $cart, $amount, @) { return $message . "How are we receiving this $amount?", \&how if keys %{ $self->{deposit_methods} }; - $cart->add(+$self->{amount}, "Deposit", { is_deposit => 1 }); + $cart + ->add(+$self->{amount}, "Deposit", { is_deposit => 1 }) + ->add_contra("-deposits/other", -$self->{amount}, "Deposited by \$you"); + return ACCEPT; } @@ -35,7 +38,15 @@ sub how :Tab(&how_tab) ($self, $cart, $input, @) { return shift @{ $how->{prompts} }, \&how_prompt; } - $cart->add(+$self->{amount}, $how->{description}, { is_deposit => 1, method => $how->{_key} }); + my $contra = + $how->{_key} eq 'cash' ? '-cash' + : $how->{_key} eq 'reimburse' ? '-expenses/reimbursed' + : "-deposits/$how->{_key}"; + + $cart + ->add(+$self->{amount}, $how->{description}, { is_deposit => 1, method => $how->{_key} }) + ->add_contra($contra, -$self->{amount}, "$how->{description} by \$you"); + return ACCEPT; } @@ -53,7 +64,11 @@ sub how_prompt($self, $cart, $input, @) { } my $desc = sprintf $how->{description}, @{ $how->{answers} }; + my $contra = $how->{_key} eq 'cash' ? '-cash' : "-deposits/$how->{_key}"; + + $cart + ->add(+$self->{amount}, $desc, { is_deposit => 1, method => $how->{_key} }) + ->add_contra($contra, -$self->{amount}, "$desc by \$you"); - $cart->add(+$self->{amount}, $desc, { is_deposit => 1, method => $how->{_key} }); return ACCEPT; } diff --git a/plugins/pfand b/plugins/pfand index e08dce9..57eafe1 100644 --- a/plugins/pfand +++ b/plugins/pfand @@ -27,7 +27,9 @@ sub product :Tab(&tab) ($self, $cart, $product, @) { or return REJECT, "Invalid pfand amount for $product"; if ($pfand) { - $cart->add(+$pfand, "Pfand zurueck", { is_return => 1 }); + $cart + ->add(+$pfand, "Pfand zurueck", { is_return => 1 }) + ->add_contra("-pfand", -$pfand, "Pfand fuer \$you"); } else { say "$product: Kein Pfand"; } @@ -44,7 +46,9 @@ sub hook_add_entry ($class, $cart, $entry, @) { my $pfand = _read_pfand->{ $entry->attribute('product_id') } or return; - $cart->add(-$pfand, "Pfand", { is_pfand => 1 }); + $cart + ->add(-$pfand, "Pfand", { is_pfand => 1 }) + ->add_contra("-pfand", +$pfand, "Pfand von \$you"); return; } diff --git a/plugins/products b/plugins/products index 297a692..32c86fa 100644 --- a/plugins/products +++ b/plugins/products @@ -42,11 +42,17 @@ sub command :Tab(edit,&tab) ($self, $cart, $command, @) { return ACCEPT; } - $cart->add( - -$price, - $product->{description}, - { product_id => $product->{id}, plugin => $self->id } - ); + $cart + ->add( + -$price, + $product->{description}, + { product_id => $product->{id}, plugin => $self->id } + ) + ->add_contra( + "+sales/products", + +$price, + "\$you bought $product->{description}" + ); return ACCEPT; } diff --git a/plugins/revspace_barcode b/plugins/revspace_barcode index 231dfb1..be2b981 100644 --- a/plugins/revspace_barcode +++ b/plugins/revspace_barcode @@ -14,11 +14,19 @@ sub command :Tab(barcode) ($self, $cart, $command, @) { } sub data($self, $cart, $input, @) { - $cart->add( - -0.07, - "Barcode <$input>", - { is_barcode => 1, barcode_data => $input } - ); + my $price = 0.07; + + $cart + ->add( + -$price, + "Barcode <$input>", + { is_barcode => 1, barcode_data => $input } + ) + ->add_contra( + "+sales/barcodes", + +$price, + "\$you bought barcode <$input>" + ); return ACCEPT; } diff --git a/plugins/revspace_bounties b/plugins/revspace_bounties index d0c7b24..483eafb 100644 --- a/plugins/revspace_bounties +++ b/plugins/revspace_bounties @@ -9,7 +9,9 @@ my %bounties = ( sub command :Tab(BOUNTY1,BOUNTY2,BOUNTY3,BOUNTY4) ($self, $cart, $command, @) { if ($command =~ /BOUNTY(\d+)/) { - $cart->add(+$bounties{$1}[0], $bounties{$1}[1]); + $cart + ->add(+$bounties{$1}[0], $bounties{$1}[1]) + ->add_contra("-expenses/bounties", -$bounties{$1}[0], "$command by \$you"); return ACCEPT; } diff --git a/plugins/revspace_mollie b/plugins/revspace_mollie index e07d7cb..460261a 100644 --- a/plugins/revspace_mollie +++ b/plugins/revspace_mollie @@ -35,11 +35,17 @@ sub command($self, $cart, $command, @) { $description .= " TEST MODE ($result->{test_amount})"; } - $cart->add( - +$amount, - $description, - { is_deposit => 1, method => 'online', mollie_id => $id, no_repeat => 1 } - ); + $cart + ->add( + +$amount, + $description, + { is_deposit => 1, method => 'online', mollie_id => $id, no_repeat => 1 } + ) + ->add_contra( + "-deposits/online", + -$amount, + "$description by \$you" + ); return ACCEPT; } diff --git a/plugins/unlisted b/plugins/unlisted index ca5437d..02709bf 100644 --- a/plugins/unlisted +++ b/plugins/unlisted @@ -13,7 +13,10 @@ sub amount($self, $cart, $arg, @) { $self->{amount} = parse_amount($arg) or return REJECT, "Invalid amount."; if ($self->{command} eq 'donate') { - $cart->add(-$self->{amount}, "Donation (THANK YOU!)"); + $cart + ->add(-$self->{amount}, "Donation (THANK YOU!)") + ->add_contra("+donations", +$self->{amount}, "Donation by \$you"); + return ACCEPT; } @@ -21,7 +24,10 @@ sub amount($self, $cart, $arg, @) { } sub description($self, $cart, $desc, @) { - $cart->add(-$self->{amount}, "Unlisted: $desc"); + $cart + ->add(-$self->{amount}, "Unlisted: $desc") + ->add_contra("+sales/unlisted", +$self->{amount}, "Unlisted: $desc by \$you"); + return ACCEPT; } diff --git a/plugins/users b/plugins/users index f1155c1..9f21357 100644 --- a/plugins/users +++ b/plugins/users @@ -28,12 +28,12 @@ sub hook_checkout($class, $cart, $user, $transaction_id, @) { } sub list($self) { - system "sort -f revbank.accounts | grep -v ^# | perl -pe's/( -[\\d.]+)/\\e[31;1m\$1\\e[0m/' | more"; + system "sort -f revbank.accounts | grep -v ^# | perl -ne's/( -[\\d.]+)/\\e[31;1m\$1\\e[0m/; print if not /^[-+]/' | more"; return ACCEPT; } sub shame($self) { - system "sort -k2 -n revbank.accounts | grep -v ^# | grep -- ' -' | perl -pe's/( -[\\d.]+)/\\e[31;1m\$1\\e[0m/' | more"; + system "sort -k2 -n revbank.accounts | grep -v ^# | grep -- ' -' | perl -ne's/( -[\\d.]+)/\\e[31;1m\$1\\e[0m/; print if not /^[-+]/' | more"; return ACCEPT; } diff --git a/revbank b/revbank index 241eff9..2bb8744 100755 --- a/revbank +++ b/revbank @@ -18,7 +18,7 @@ use RevBank::Global; use RevBank::Messages; use RevBank::Cart; -our $VERSION = "3.3"; +our $VERSION = "3.4"; our %HELP1 = ( "abort" => "Abort the current transaction", );