fix(machine): seed cassettes on first boot + add atm-transactions script
Cassette inventory was never initialized in SQLite, so recordTransaction's UPDATE decrements were no-ops against an empty table. Now the Electron main process seeds cassettes from VITE_LAMASSU_CASSETTES or the model preset on first boot. Also adds an atm-transactions CLI script (with --summary, --inventory, --type, --today, --last, --since filters) and sqlite to the NixOS system packages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f6a1c2f9f6
commit
e63f8ab9f6
3 changed files with 189 additions and 0 deletions
141
deploy/nixos/atm-transactions.sh
Normal file
141
deploy/nixos/atm-transactions.sh
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#!/usr/bin/env bash
|
||||
# atm-transactions — Query ATM transaction history from SQLite
|
||||
#
|
||||
# Usage:
|
||||
# atm-transactions # Show all transactions
|
||||
# atm-transactions --today # Today's transactions
|
||||
# atm-transactions --type cash_in # Only cash-in
|
||||
# atm-transactions --type cash_out # Only cash-out
|
||||
# atm-transactions --last 5 # Last 5 transactions
|
||||
# atm-transactions --since 2026-02-28 # Since a date
|
||||
# atm-transactions --summary # Summary totals only
|
||||
# atm-transactions --inventory # Current cassette & cashbox state
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DB="/var/lib/lamassu-atm/state.db"
|
||||
|
||||
if [ ! -f "$DB" ]; then
|
||||
echo "ERROR: Database not found at $DB" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sql() {
|
||||
sqlite3 -column -header "$DB" "$1"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
TYPE=""
|
||||
LIMIT=""
|
||||
SINCE=""
|
||||
SUMMARY=false
|
||||
INVENTORY=false
|
||||
TODAY=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--type)
|
||||
TYPE="$2"; shift 2 ;;
|
||||
--last)
|
||||
LIMIT="$2"; shift 2 ;;
|
||||
--since)
|
||||
SINCE="$2"; shift 2 ;;
|
||||
--today)
|
||||
TODAY=true; shift ;;
|
||||
--summary)
|
||||
SUMMARY=true; shift ;;
|
||||
--inventory)
|
||||
INVENTORY=true; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: atm-transactions [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --type cash_in|cash_out Filter by transaction type"
|
||||
echo " --last N Show last N transactions"
|
||||
echo " --since YYYY-MM-DD Show transactions since date"
|
||||
echo " --today Show today's transactions"
|
||||
echo " --summary Show summary totals"
|
||||
echo " --inventory Show cassette & cashbox state"
|
||||
echo " -h, --help Show this help"
|
||||
exit 0 ;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Inventory mode
|
||||
if $INVENTORY; then
|
||||
echo "=== Cassettes ==="
|
||||
sql "SELECT denomination AS 'Denom', count AS 'Bills', denomination * count AS 'Value' FROM cassettes ORDER BY denomination;"
|
||||
echo ""
|
||||
echo "=== Cashbox ==="
|
||||
sql "SELECT total_bills AS 'Bills', total_fiat_cents / 100 AS 'Total', CASE WHEN last_emptied_at IS NOT NULL THEN datetime(last_emptied_at / 1000, 'unixepoch') ELSE 'never' END AS 'Last Emptied' FROM cashbox;"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Summary mode
|
||||
if $SUMMARY; then
|
||||
echo "=== Transaction Summary ==="
|
||||
sql "SELECT
|
||||
type AS 'Type',
|
||||
COUNT(*) AS 'Count',
|
||||
SUM(fiat_cents) / 100 AS 'Total Fiat',
|
||||
SUM(sats) AS 'Total Sats',
|
||||
MIN(fiat_cents) / 100 AS 'Min Fiat',
|
||||
MAX(fiat_cents) / 100 AS 'Max Fiat'
|
||||
FROM transactions
|
||||
GROUP BY type;"
|
||||
echo ""
|
||||
echo "=== Daily Breakdown ==="
|
||||
sql "SELECT
|
||||
date(created_at / 1000, 'unixepoch') AS 'Date',
|
||||
type AS 'Type',
|
||||
COUNT(*) AS 'Txns',
|
||||
SUM(fiat_cents) / 100 AS 'Fiat',
|
||||
SUM(sats) AS 'Sats'
|
||||
FROM transactions
|
||||
GROUP BY date(created_at / 1000, 'unixepoch'), type
|
||||
ORDER BY 1 DESC, 2;"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build WHERE clause
|
||||
WHERE=""
|
||||
CONDITIONS=()
|
||||
|
||||
if [ -n "$TYPE" ]; then
|
||||
CONDITIONS+=("type = '$TYPE'")
|
||||
fi
|
||||
|
||||
if $TODAY; then
|
||||
CONDITIONS+=("date(created_at / 1000, 'unixepoch') = date('now')")
|
||||
fi
|
||||
|
||||
if [ -n "$SINCE" ]; then
|
||||
CONDITIONS+=("date(created_at / 1000, 'unixepoch') >= '$SINCE'")
|
||||
fi
|
||||
|
||||
if [ ${#CONDITIONS[@]} -gt 0 ]; then
|
||||
WHERE="WHERE $(IFS=' AND '; echo "${CONDITIONS[*]}")"
|
||||
fi
|
||||
|
||||
# Build LIMIT clause
|
||||
LIMIT_SQL=""
|
||||
if [ -n "$LIMIT" ]; then
|
||||
LIMIT_SQL="LIMIT $LIMIT"
|
||||
fi
|
||||
|
||||
# Main query
|
||||
sql "SELECT
|
||||
t.txid AS 'TX ID',
|
||||
t.type AS 'Type',
|
||||
t.fiat_cents / 100 AS 'Fiat',
|
||||
t.sats AS 'Sats',
|
||||
datetime(t.created_at / 1000, 'unixepoch') AS 'Time (UTC)',
|
||||
group_concat('Q' || tb.denomination || 'x' || tb.count) AS 'Bills'
|
||||
FROM transactions t
|
||||
LEFT JOIN transaction_bills tb ON t.txid = tb.txid
|
||||
$WHERE
|
||||
GROUP BY t.txid
|
||||
ORDER BY t.created_at DESC
|
||||
$LIMIT_SQL;"
|
||||
|
|
@ -109,6 +109,10 @@
|
|||
# Camera support
|
||||
v4l-utils
|
||||
fswebcam
|
||||
|
||||
# ATM operations
|
||||
sqlite
|
||||
(writeShellScriptBin "atm-transactions" (builtins.readFile ./atm-transactions.sh))
|
||||
];
|
||||
|
||||
# Enable SSH for remote administration
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue