Account
NAME
App::Moneymoor::Gateway::Account - SQL gateway for accounts, and the owner of the credit-card payment-category invariant.
SYNOPSIS
use App::Moneymoor::Gateway::Account;
my $gw = App::Moneymoor::Gateway::Account.new(:$db);
my $current = $gw.create(App::Moneymoor::Model::Account.new(
name => 'Current Account', type => 'cash',
));
# Creating a credit account also creates its payment envelope, in the
# same SQL transaction ā there is no window in which one exists
# without the other.
my $visa = $gw.create(App::Moneymoor::Model::Account.new(
name => 'Visa', type => 'credit',
));
my $envelope = $gw.payment-category-for($visa.id);
say $envelope.name; # Visa
my @open = $gw.find-all; # closed accounts excluded
my @all = $gw.find-all(:include-closed);
$gw.close($visa.id); # soft retire, history intact
$gw.reopen($visa.id);
my $dup = $gw.create(App::Moneymoor::Model::Account.new(name => 'Visa'));
say $dup ~~ Failure; # True ā names are unique
$dup.so;
DESCRIPTION
Accounts are the one entity whose creation has a side effect, and it
is a load-bearing one: every credit account owns exactly one
payment category, created with it and deleted with it. The budget
derivation relies on that ā a credit account with no payment envelope
has nowhere to reserve the cash its spending commits, so
Service::Budget refuses to let its transactions reach the budget
at all (and says so in warnings). Doing the two inserts inside one
run-txn is what makes the invariant true rather than usually true.
The payment envelope is named after its account and lives in the
system Credit Card Payments group, which the migrations seed. If
that group has somehow been deleted, create recreates it rather
than failing: an ungrouped payment category is a display problem, a
missing one is a correctness problem.
VALIDATION AND FAILURE
Every method validates before opening a transaction and returns a
Failure (via fail) for anything it will not do ā duplicate
name, empty name, unknown type, unknown id. A Failure returned
from inside a transaction would be a silent commit of a half-applied
change, so the rule is: validate first, then write.
my $r = $gw.create($account);
if $r ~~ Failure { note $r.exception.message; $r.so }
WHAT UPDATE WILL NOT DO
update changes name, note, closed and sort-order. It
refuses to change type, and that is deliberate: every type
transition has a different correct behaviour (cash ā credit must mint
a payment envelope; credit ā cash must retire one that may already
hold money and be referenced by splits; anything ā tracking must pull
existing transactions back out of the budget). Silently picking one
of those would corrupt history. Delete the account and re-create it,
or wait for a version that offers explicit conversions.
DELETION
delete is a hard delete and it takes the account's transactions
(and their splits) with it, plus the payment category and any money
assigned to it. It is refused when the payment category is referenced
by splits belonging to other accounts' transactions ā that would
rewrite those transactions' history ā with a message naming the
problem. Prefer close for anything you still want to see.
ATTRIBUTES
dbā requiredApp::Moneymoor::DB.
METHODS
find-all(Bool :$include-closed = False --Array)>find-by-id(Int:D $id --Model::Account)> ā type object when absent.find-by-name(Str:D $name --Model::Account)>create(Model::Account:D $account --Model::Account)>update(Model::Account:D $account)close(Int:D $id)/reopen(Int:D $id)delete(Int:D $id)payment-category-for(Int:D $account-id --Model::Category)>