Compare commits
No commits in common. "master" and "v0.1" have entirely different histories.
10 changed files with 72 additions and 553 deletions
90
0PLAN.org
90
0PLAN.org
|
@ -1,90 +0,0 @@
|
||||||
#+title: Frontend
|
|
||||||
|
|
||||||
* Plan
|
|
||||||
|
|
||||||
** 0.3
|
|
||||||
|
|
||||||
- [ ] versioned schema support
|
|
||||||
- [ ] barschema table
|
|
||||||
- [ ] function to check
|
|
||||||
- [ ] function to set
|
|
||||||
- [ ] initial creation of tables at version 1 assuming OK if tables exist
|
|
||||||
- [ ] users import from hackerbase
|
|
||||||
|
|
||||||
** 0.4
|
|
||||||
|
|
||||||
- [ ] add inventory check table
|
|
||||||
- [ ] add inventory item check table
|
|
||||||
- [ ] create new check
|
|
||||||
- [ ] check item
|
|
||||||
- [ ] close check
|
|
||||||
- [ ] inventory check component
|
|
||||||
|
|
||||||
** Later
|
|
||||||
|
|
||||||
- [ ] add static token support
|
|
||||||
- [ ] hardwired token for the React.JS app?
|
|
||||||
- [ ] authentication
|
|
||||||
- [ ] login/logout
|
|
||||||
- [ ] allow API calls only with login token
|
|
||||||
|
|
||||||
* Finished
|
|
||||||
|
|
||||||
** 0.1
|
|
||||||
|
|
||||||
- [X] statically compiled binary HTTP server
|
|
||||||
- [X] include directory tree contents
|
|
||||||
- [X] handle frontend with mime type
|
|
||||||
- [X] move app to / again
|
|
||||||
- [X] load certificates
|
|
||||||
- [X] deployment preparations
|
|
||||||
- [X] crosscompilation - no, compile in qemu or on separate host
|
|
||||||
- [X] connect to postgres
|
|
||||||
- [X] use qr-scanner - no, qr-barcode-scanner is needed
|
|
||||||
- [X] rsync + build in qemu or host without react (filters or something)
|
|
||||||
- [X] API infrastructure
|
|
||||||
- [X] handle API calls
|
|
||||||
- [X] API registry syntax
|
|
||||||
- [X] lookup barcode in DB
|
|
||||||
- [X] separate module brmbar-data for queries
|
|
||||||
- [X] separate module api-servlets
|
|
||||||
|
|
||||||
** 0.2
|
|
||||||
|
|
||||||
- [X] use localStorage to store user
|
|
||||||
- [X] add barcode scanner to get the user
|
|
||||||
- [X] allow editing
|
|
||||||
- [X] add UserSelect component
|
|
||||||
- [X] integrate script for starting build qemu system
|
|
||||||
- [X] add org file to the repository after cleanup
|
|
||||||
|
|
||||||
* Qemu
|
|
||||||
|
|
||||||
#+BEGIN_SRC
|
|
||||||
wget img zip # 2019-04-08-raspbian-stretch.zip
|
|
||||||
unzip 2019-04-08-raspbian-stretch.zip
|
|
||||||
sudo mkdir /mnt/image
|
|
||||||
sudo mount -o loop,offset=4194304 2019-04-08-raspbian-stretch.img /mnt/image/
|
|
||||||
# In qemu-system-armhf terminal!
|
|
||||||
cp kernel, DT
|
|
||||||
umount /mnt/image
|
|
||||||
resize img
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
sources.list - archive debian (remove rpi), archive raspberry
|
|
||||||
https://archive.raspberrypi.org/debian/
|
|
||||||
https://archive.debian.org/debian/
|
|
||||||
apt-get --no
|
|
||||||
|
|
||||||
* React - Vite
|
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
|
||||||
npm create vite@latest
|
|
||||||
# "frontend"
|
|
||||||
# React
|
|
||||||
# Javascript
|
|
||||||
cd frontend
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
||||||
npm run build
|
|
||||||
#+END_SRC
|
|
|
@ -47,7 +47,6 @@
|
||||||
(define -db-user- (make-parameter #f))
|
(define -db-user- (make-parameter #f))
|
||||||
(define -db-name- (make-parameter #f))
|
(define -db-name- (make-parameter #f))
|
||||||
(define -db-pass- (make-parameter #f))
|
(define -db-pass- (make-parameter #f))
|
||||||
(define -db-enabled- (make-parameter #t))
|
|
||||||
|
|
||||||
(command-line
|
(command-line
|
||||||
print-help
|
print-help
|
||||||
|
@ -83,8 +82,6 @@
|
||||||
(-db-user- dbuser))
|
(-db-user- dbuser))
|
||||||
(-dp (dbpass) "Database password"
|
(-dp (dbpass) "Database password"
|
||||||
(-db-pass- dbpass))
|
(-db-pass- dbpass))
|
||||||
(-dd () "Disable database"
|
|
||||||
(-db-enabled- #f))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
(define ssl? (and (-certificate-) (-key-) #t))
|
(define ssl? (and (-certificate-) (-key-) #t))
|
||||||
|
@ -123,8 +120,7 @@
|
||||||
(print "current user id: " (current-user-id))
|
(print "current user id: " (current-user-id))
|
||||||
(print "current effective user id: " (current-effective-user-id))
|
(print "current effective user id: " (current-effective-user-id))
|
||||||
|
|
||||||
(when (-db-enabled-)
|
(bar-db-init! (-db-name-) (-db-host-) (-db-user-) (-db-pass-))
|
||||||
(bar-db-init! (-db-name-) (-db-host-) (-db-user-) (-db-pass-)))
|
|
||||||
|
|
||||||
(define (handle-api-calls)
|
(define (handle-api-calls)
|
||||||
(define plst (cdr (uri-path (request-uri (current-request)))))
|
(define plst (cdr (uri-path (request-uri (current-request)))))
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
(chicken format))
|
(chicken format))
|
||||||
|
|
||||||
;; Short banner
|
;; Short banner
|
||||||
(define banner-line "BrmInv 0.2 (c) 2023-2025 Brmlab, z.s.")
|
(define banner-line "BrmInv 0.1 (c) 2023-2025 Brmlab, z.s.")
|
||||||
|
|
||||||
;; The license of this file and of the whole suite.
|
;; The license of this file and of the whole suite.
|
||||||
(define license "ISC License
|
(define license "ISC License
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.App {
|
.App {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
color: var(--primary-text-color) !important;
|
color: var(--primary-text-color) !important;
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
width: 10vh;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,88 +1,13 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import BarcodeScannerComponent from 'react-qr-barcode-scanner';
|
import BarcodeScannerComponent from 'react-qr-barcode-scanner';
|
||||||
import Container from 'react-bootstrap/Container';
|
import { Container, Row, Col } from 'react-bootstrap';
|
||||||
import Row from 'react-bootstrap/Row';
|
|
||||||
import Col from 'react-bootstrap/Col';
|
|
||||||
import Table from 'react-bootstrap/Table';
|
import Table from 'react-bootstrap/Table';
|
||||||
import Alert from 'react-bootstrap/Alert';
|
import Alert from 'react-bootstrap/Alert';
|
||||||
import Form from 'react-bootstrap/Form';
|
|
||||||
import Button from 'react-bootstrap/Button';
|
|
||||||
import Card from 'react-bootstrap/Card';
|
|
||||||
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// Ensure we have persistent (informal) user information
|
|
||||||
const [user, setUser] = useState("");
|
|
||||||
useEffect(() => {
|
|
||||||
const user = localStorage.getItem('user');
|
|
||||||
if (user) {
|
|
||||||
setUser(user);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem('user', user);
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
// If no user, must be scanned/set otherwise
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{user === "" ?
|
|
||||||
<NoUserView setUser={setUser} />
|
|
||||||
:
|
|
||||||
<UserView user={user} setUser={setUser} />}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function NoUserView({setUser}) {
|
|
||||||
const [preUser, setPreUser] = useState("");
|
|
||||||
return (
|
|
||||||
<Card bg="info" style={{width: '100vw', height: '100vh'}}>
|
|
||||||
<Card.Body>
|
|
||||||
<Card.Title>BrmInv Anonymous</Card.Title>
|
|
||||||
<Form.Group>
|
|
||||||
<Form.Label>User name:</Form.Label>
|
|
||||||
<Form.Control type="text" placeholder="Username"
|
|
||||||
value={preUser} onChange={(v) => setPreUser(v.target.value)} />
|
|
||||||
<Form.Text>Enter a user name or scan your barcode.</Form.Text>
|
|
||||||
</Form.Group>
|
|
||||||
<Card.Text>
|
|
||||||
<BarcodeScannerComponent
|
|
||||||
style={{width: 'auto', height: 'auto'}}
|
|
||||||
onUpdate={(err, result) => {
|
|
||||||
if (result) {
|
|
||||||
setPreUser(result.text);
|
|
||||||
}}}
|
|
||||||
delay={1500}
|
|
||||||
/>
|
|
||||||
</Card.Text>
|
|
||||||
</Card.Body>
|
|
||||||
<Card.Footer>
|
|
||||||
<Button variant="primary"
|
|
||||||
style={{width: '100%'}}
|
|
||||||
onClick={() => setUser(preUser)}>Set!</Button>
|
|
||||||
</Card.Footer>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function UserView({user, setUser}) {
|
|
||||||
return (
|
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
{user}
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<Button variant="info" onClick={() => setUser('')}>Change</Button>
|
|
||||||
</Col>
|
|
||||||
<BarItemScanner />
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BarItemScanner() {
|
|
||||||
const [reqAccount, setReqAccount] = useState('');
|
const [reqAccount, setReqAccount] = useState('');
|
||||||
const [actAccount, setActAccount] = useState('');
|
const [actAccount, setActAccount] = useState('');
|
||||||
const [balance, setBalance] = useState(-1);
|
const [balance, setBalance] = useState(-1);
|
||||||
|
@ -112,7 +37,7 @@ function BarItemScanner() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<BarcodeScannerComponent
|
<BarcodeScannerComponent
|
||||||
|
@ -146,7 +71,7 @@ function BarItemScanner() {
|
||||||
<Alert variant={msgType}>{statusMsg}</Alert>
|
<Alert variant={msgType}>{statusMsg}</Alert>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
install-eggs-arm.sh
Normal file
64
install-eggs-arm.sh
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# install-eggs.sh
|
||||||
|
#
|
||||||
|
# Local installer of CHICKEN eggs required for building.
|
||||||
|
#
|
||||||
|
# ISC License
|
||||||
|
#
|
||||||
|
# Copyright 2023 Brmlab, z.s.
|
||||||
|
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software
|
||||||
|
# for any purpose with or without fee is hereby granted, provided
|
||||||
|
# that the above copyright notice and this permission notice appear
|
||||||
|
# in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||||
|
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Source root directory
|
||||||
|
owd=$(pwd)
|
||||||
|
cd $(dirname "$0")
|
||||||
|
SRCDIR=$(pwd)
|
||||||
|
cd "$owd"
|
||||||
|
|
||||||
|
# Make temporary prefix directory (eggs shared throwaway files)
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
|
||||||
|
# Installs given egg locally
|
||||||
|
chicken_install() {
|
||||||
|
echo "Installing $1 ..."
|
||||||
|
# CHICKEN_INSTALL_PREFIX="$TMPDIR" \
|
||||||
|
# CHICKEN_REPOSITORY_PATH="$SRCDIR/eggs-arm":`./cross-chicken-arm/bin/arm-chicken-install -repository` \
|
||||||
|
# CHICKEN_INSTALL_REPOSITORY="$SRCDIR/eggs-arm" \
|
||||||
|
# ./cross-chicken-arm/bin/arm-chicken-install "$1" 2>&1 | \
|
||||||
|
# sed -u 's/^/ /'
|
||||||
|
# CHICKEN_INSTALL_PREFIX="$TMPDIR" \
|
||||||
|
./cross-chicken-arm/bin/arm-chicken-install "$1" 2>&1 | \
|
||||||
|
sed -u 's/^/ /'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removes throwaway files
|
||||||
|
chicken_cleanup() {
|
||||||
|
echo "Cleaning up ..."
|
||||||
|
rm -fr ${TMPDIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Always cleanup
|
||||||
|
trap chicken_cleanup INT QUIT
|
||||||
|
|
||||||
|
# Install required eggs
|
||||||
|
chicken_install spiffy
|
||||||
|
chicken_install openssl
|
||||||
|
chicken_install postgresql
|
||||||
|
|
||||||
|
# Normal termination cleanup
|
||||||
|
chicken_cleanup
|
|
@ -57,7 +57,6 @@ chicken_install openssl
|
||||||
chicken_install spiffy
|
chicken_install spiffy
|
||||||
chicken_install postgresql
|
chicken_install postgresql
|
||||||
chicken_install json
|
chicken_install json
|
||||||
chicken_install posix-groups
|
|
||||||
|
|
||||||
# Normal termination cleanup
|
# Normal termination cleanup
|
||||||
chicken_cleanup
|
chicken_cleanup
|
||||||
|
|
|
@ -1,313 +0,0 @@
|
||||||
--
|
|
||||||
-- 0000-init.sql
|
|
||||||
--
|
|
||||||
-- Initial SQL schema construction as of 2025-04-20 (or so)
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- Dominik Pantůček <dominik.pantucek@trustica.cz>
|
|
||||||
--
|
|
||||||
-- Permission to use, copy, modify, and/or distribute this software
|
|
||||||
-- for any purpose with or without fee is hereby granted, provided
|
|
||||||
-- that the above copyright notice and this permission notice appear
|
|
||||||
-- in all copies.
|
|
||||||
--
|
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
||||||
-- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
||||||
-- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
||||||
-- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
||||||
-- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
-- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
-- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
||||||
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- To require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
-- Privileged schema with protected data
|
|
||||||
CREATE SCHEMA IF NOT EXISTS brmbar_privileged;
|
|
||||||
|
|
||||||
-- Initial versioning
|
|
||||||
CREATE TABLE IF NOT EXISTS brmbar_privileged.brmbar_schema(
|
|
||||||
ver INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------
|
|
||||||
-- Legacy Schema Initialization
|
|
||||||
-- ----------------------------------------------------------------
|
|
||||||
|
|
||||||
DO $$
|
|
||||||
DECLARE v INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT ver FROM brmbar_privileged.brmbar_schema INTO v;
|
|
||||||
IF v IS NULL THEN
|
|
||||||
-- --------------------------------
|
|
||||||
-- Legacy Types
|
|
||||||
|
|
||||||
SELECT COUNT(*) INTO v
|
|
||||||
FROM pg_catalog.pg_type typ
|
|
||||||
INNER JOIN pg_catalog.pg_namespace nsp
|
|
||||||
ON nsp.oid = typ.typnamespace
|
|
||||||
WHERE nsp.nspname = 'public'
|
|
||||||
AND typ.typname='exchange_rate_direction';
|
|
||||||
IF v=0 THEN
|
|
||||||
RAISE NOTICE 'Creating type exchange_rate_direction';
|
|
||||||
CREATE TYPE public.exchange_rate_direction
|
|
||||||
AS ENUM ('source_to_target', 'target_to_source');
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'Type exchange_rate_direction already exists';
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
SELECT COUNT(*) INTO v
|
|
||||||
FROM pg_catalog.pg_type typ
|
|
||||||
INNER JOIN pg_catalog.pg_namespace nsp
|
|
||||||
ON nsp.oid = typ.typnamespace
|
|
||||||
WHERE nsp.nspname = 'public'
|
|
||||||
AND typ.typname='account_type';
|
|
||||||
IF v=0 THEN
|
|
||||||
RAISE NOTICE 'Creating type account_type';
|
|
||||||
CREATE TYPE public.account_type
|
|
||||||
AS ENUM ('cash', 'debt', 'inventory', 'income', 'expense',
|
|
||||||
'starting_balance', 'ending_balance');
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'Type account_type already exists';
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
SELECT COUNT(*) INTO v
|
|
||||||
FROM pg_catalog.pg_type typ
|
|
||||||
INNER JOIN pg_catalog.pg_namespace nsp
|
|
||||||
ON nsp.oid = typ.typnamespace
|
|
||||||
WHERE nsp.nspname = 'public'
|
|
||||||
AND typ.typname='transaction_split_side';
|
|
||||||
IF v=0 THEN
|
|
||||||
RAISE NOTICE 'Creating type transaction_split_side';
|
|
||||||
CREATE TYPE public.transaction_split_side
|
|
||||||
AS ENUM ('credit', 'debit');
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'Type transaction_split_side already exists';
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Currencies sequence, table and potential initial data
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.currencies_id_seq
|
|
||||||
START WITH 2 INCREMENT BY 1;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.currencies (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('public.currencies_id_seq'::regclass),
|
|
||||||
name VARCHAR(128) NOT NULL,
|
|
||||||
UNIQUE(name)
|
|
||||||
);
|
|
||||||
INSERT INTO public.currencies (id, name) VALUES (1, 'Kč')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Exchange rates table - no initial data required
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.exchange_rates (
|
|
||||||
valid_since TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
|
||||||
|
|
||||||
target INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (target) REFERENCES public.currencies (id),
|
|
||||||
|
|
||||||
source INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (source) REFERENCES public.currencies (id),
|
|
||||||
|
|
||||||
rate DECIMAL(12,2) NOT NULL,
|
|
||||||
rate_dir public.exchange_rate_direction NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Accounts sequence and table and 4 initial accounts
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.accounts_id_seq
|
|
||||||
START WITH 2 INCREMENT BY 1;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.accounts (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('public.accounts_id_seq'::regclass),
|
|
||||||
|
|
||||||
name VARCHAR(128) NOT NULL,
|
|
||||||
UNIQUE (name),
|
|
||||||
|
|
||||||
currency INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (currency) REFERENCES public.currencies (id),
|
|
||||||
|
|
||||||
acctype public.account_type NOT NULL,
|
|
||||||
|
|
||||||
active BOOLEAN NOT NULL DEFAULT TRUE
|
|
||||||
);
|
|
||||||
INSERT INTO public.accounts (id, name, currency, acctype)
|
|
||||||
VALUES (1, 'BrmBar Cash', (SELECT id FROM public.currencies WHERE name='Kč'), 'cash')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
INSERT INTO public.accounts (name, currency, acctype)
|
|
||||||
VALUES ('BrmBar Profits', (SELECT id FROM public.currencies WHERE name='Kč'), 'income')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
INSERT INTO public.accounts (name, currency, acctype)
|
|
||||||
VALUES ('BrmBar Excess', (SELECT id FROM public.currencies WHERE name='Kč'), 'income')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
INSERT INTO public.accounts (name, currency, acctype)
|
|
||||||
VALUES ('BrmBar Deficit', (SELECT id FROM public.currencies WHERE name='Kč'), 'expense')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Barcodes
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.barcodes (
|
|
||||||
barcode VARCHAR(128) PRIMARY KEY NOT NULL,
|
|
||||||
|
|
||||||
account INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (account) REFERENCES public.accounts (id)
|
|
||||||
);
|
|
||||||
INSERT INTO public.barcodes (barcode, account)
|
|
||||||
VALUES ('_cash_', (SELECT id FROM public.accounts WHERE acctype = 'cash'))
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Transactions
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.transactions_id_seq
|
|
||||||
START WITH 1 INCREMENT BY 1;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.transactions (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('public.transactions_id_seq'::regclass),
|
|
||||||
time TIMESTAMP DEFAULT NOW() NOT NULL,
|
|
||||||
|
|
||||||
responsible INTEGER,
|
|
||||||
FOREIGN KEY (responsible) REFERENCES public.accounts (id),
|
|
||||||
|
|
||||||
description TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Transaction splits
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.transaction_splits_id_seq
|
|
||||||
START WITH 1 INCREMENT BY 1;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.transaction_splits (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('public.transaction_splits_id_seq'::regclass),
|
|
||||||
|
|
||||||
transaction INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (transaction) REFERENCES public.transactions (id),
|
|
||||||
|
|
||||||
side public.transaction_split_side NOT NULL,
|
|
||||||
|
|
||||||
account INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (account) REFERENCES public.accounts (id),
|
|
||||||
amount DECIMAL(12,2) NOT NULL,
|
|
||||||
|
|
||||||
memo TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Account balances view
|
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW public.account_balances AS
|
|
||||||
SELECT ts.account AS id,
|
|
||||||
accounts.name,
|
|
||||||
accounts.acctype,
|
|
||||||
- sum(
|
|
||||||
CASE
|
|
||||||
WHEN ts.side = 'credit'::public.transaction_split_side THEN - ts.amount
|
|
||||||
ELSE ts.amount
|
|
||||||
END) AS crbalance
|
|
||||||
FROM public.transaction_splits ts
|
|
||||||
LEFT JOIN public.accounts ON accounts.id = ts.account
|
|
||||||
GROUP BY ts.account, accounts.name, accounts.acctype
|
|
||||||
ORDER BY (- sum(
|
|
||||||
CASE
|
|
||||||
WHEN ts.side = 'credit'::public.transaction_split_side THEN - ts.amount
|
|
||||||
ELSE ts.amount
|
|
||||||
END));
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Transaction nice splits view
|
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW public.transaction_nicesplits AS
|
|
||||||
SELECT ts.id,
|
|
||||||
ts.transaction,
|
|
||||||
ts.account,
|
|
||||||
CASE
|
|
||||||
WHEN ts.side = 'credit'::public.transaction_split_side THEN - ts.amount
|
|
||||||
ELSE ts.amount
|
|
||||||
END AS amount,
|
|
||||||
a.currency,
|
|
||||||
ts.memo
|
|
||||||
FROM public.transaction_splits ts
|
|
||||||
LEFT JOIN public.accounts a ON a.id = ts.account
|
|
||||||
ORDER BY ts.id;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Transaction cash sums view
|
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW public.transaction_cashsums AS
|
|
||||||
SELECT t.id,
|
|
||||||
t."time",
|
|
||||||
sum(credit.credit_cash) AS cash_credit,
|
|
||||||
sum(debit.debit_cash) AS cash_debit,
|
|
||||||
a.name AS responsible,
|
|
||||||
t.description
|
|
||||||
FROM public.transactions t
|
|
||||||
LEFT JOIN ( SELECT cts.amount AS credit_cash,
|
|
||||||
cts.transaction AS cts_t
|
|
||||||
FROM public.transaction_nicesplits cts
|
|
||||||
LEFT JOIN public.accounts a_1 ON a_1.id = cts.account OR a_1.id = cts.account
|
|
||||||
WHERE a_1.currency = (( SELECT accounts.currency
|
|
||||||
FROM public.accounts
|
|
||||||
WHERE accounts.name::text = 'BrmBar Cash'::text))
|
|
||||||
AND (a_1.acctype = ANY (ARRAY['cash'::public.account_type, 'debt'::public.account_type]))
|
|
||||||
AND cts.amount < 0::numeric) credit ON credit.cts_t = t.id
|
|
||||||
LEFT JOIN ( SELECT dts.amount AS debit_cash,
|
|
||||||
dts.transaction AS dts_t
|
|
||||||
FROM public.transaction_nicesplits dts
|
|
||||||
LEFT JOIN public.accounts a_1 ON a_1.id = dts.account OR a_1.id = dts.account
|
|
||||||
WHERE a_1.currency = (( SELECT accounts.currency
|
|
||||||
FROM public.accounts
|
|
||||||
WHERE accounts.name::text = 'BrmBar Cash'::text))
|
|
||||||
AND (a_1.acctype = ANY (ARRAY['cash'::public.account_type, 'debt'::public.account_type]))
|
|
||||||
AND dts.amount > 0::numeric) debit ON debit.dts_t = t.id
|
|
||||||
LEFT JOIN public.accounts a ON a.id = t.responsible
|
|
||||||
GROUP BY t.id, a.name
|
|
||||||
ORDER BY t.id DESC;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
-- Function to check schema version (used in migrations)
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.has_exact_schema_version(
|
|
||||||
IN i_ver INTEGER
|
|
||||||
) RETURNS INTEGER
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $x$
|
|
||||||
DECLARE
|
|
||||||
v_ver INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT ver INTO v_ver FROM brmbar_privileged.brmbar_schema;
|
|
||||||
IF v_ver is NULL THEN
|
|
||||||
RETURN false;
|
|
||||||
ELSE
|
|
||||||
RETURN v_ver = i_ver;
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$x$;
|
|
||||||
|
|
||||||
-- --------------------------------
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.upgrade_schema_version_to(
|
|
||||||
IN i_ver INTEGER
|
|
||||||
) RETURNS INTEGER
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $x$
|
|
||||||
DECLARE
|
|
||||||
v_ver INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT ver FROM brmbar_privileged.brmbar_schema INTO v_ver;
|
|
||||||
IF v_ver=(i_ver-1) THEN
|
|
||||||
UPDATE brmbar_privileged.brmbar_schema SET ver = i_ver;
|
|
||||||
ELSE
|
|
||||||
RAISE EXCEPTION 'Invalid brmbar schema version transition (% -> %)', v_ver, i_ver;
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$x$;
|
|
||||||
|
|
||||||
-- Initialize version 1
|
|
||||||
INSERT INTO brmbar_privileged.brmbar_schema(ver) VALUES(1);
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$$;
|
|
|
@ -1,28 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
|
||||||
# build-in-qemu.sh
|
|
||||||
#
|
|
||||||
# Expects running armhf qemu system, builds the binary inside.
|
|
||||||
#
|
|
||||||
# ISC License
|
|
||||||
#
|
|
||||||
# Copyright 2025 Brmlab, z.s.
|
|
||||||
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software
|
|
||||||
# for any purpose with or without fee is hereby granted, provided
|
|
||||||
# that the above copyright notice and this permission notice appear
|
|
||||||
# in all copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
||||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
||||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
||||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
||||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
if [ -z "$1" ] ; then
|
if [ -z "$1" ] ; then
|
||||||
echo "Usage: $0 password"
|
echo "Usage: $0 password"
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# run-build-qemu-system.sh
|
|
||||||
#
|
|
||||||
# Runs the emulated armhf system for building the application.
|
|
||||||
#
|
|
||||||
# ISC License
|
|
||||||
#
|
|
||||||
# Copyright 2025 Brmlab, z.s.
|
|
||||||
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software
|
|
||||||
# for any purpose with or without fee is hereby granted, provided
|
|
||||||
# that the above copyright notice and this permission notice appear
|
|
||||||
# in all copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
||||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
||||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
||||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
||||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
||||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
qemu-system-armhf \
|
|
||||||
-machine raspi2b \
|
|
||||||
-nographic \
|
|
||||||
-dtb bcm2710-rpi-3-b-plus.dtb \
|
|
||||||
-m 1G \
|
|
||||||
-smp 4 \
|
|
||||||
-kernel kernel7.img \
|
|
||||||
-sd 2019-04-08-raspbian-stretch.img \
|
|
||||||
-append "rw earlyprintk loglevel=8 console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootdelay=1 dwc_otg.fiq_fsm_enable=0" \
|
|
||||||
-usb \
|
|
||||||
-device usb-net,netdev=net0 \
|
|
||||||
-netdev user,id=net0,hostfwd=tcp::2222-:22
|
|
Loading…
Add table
Add a link
Reference in a new issue