forked from brmlab/brmbar-github
Compare commits
5 commits
master
...
test_cache
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5bc7b0e004 | ||
![]() |
96bf23662e | ||
![]() |
f922edf042 | ||
![]() |
0d2bb2eed7 | ||
![]() |
15bb03e5fe |
51 changed files with 194 additions and 2902 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
.*.sw?
|
.*.sw?
|
||||||
*~
|
|
||||||
|
|
2
barcode-generator/.gitignore
vendored
2
barcode-generator/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
barcodes*.svg
|
|
||||||
barcode-generator.txt
|
|
|
@ -1,51 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
#
|
|
||||||
# requires zint binary from zint package
|
|
||||||
#
|
|
||||||
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
import sys
|
|
||||||
|
|
||||||
svghead = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" height="1052.3622" width="744.09448" version="1.1" id="svg2" inkscape:version="0.47 r22583" sodipodi:docname="barcodes.svg">
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
svgfoot = """</svg>
|
|
||||||
"""
|
|
||||||
|
|
||||||
width = 5
|
|
||||||
scalex = 0.8
|
|
||||||
scaley = 0.8
|
|
||||||
|
|
||||||
p = 0
|
|
||||||
i = 0
|
|
||||||
j = 0
|
|
||||||
f = None
|
|
||||||
|
|
||||||
lines = sys.stdin.readlines()
|
|
||||||
|
|
||||||
for idx in xrange(len(lines)):
|
|
||||||
items = lines[idx].strip().split(';')
|
|
||||||
if idx % 30 == 0:
|
|
||||||
if f and not f.closed:
|
|
||||||
f.write(svgfoot)
|
|
||||||
f.close()
|
|
||||||
f = open('barcodes' + str(p) + '.svg','w')
|
|
||||||
p += 1
|
|
||||||
i = 0
|
|
||||||
j = 0
|
|
||||||
f.write(svghead)
|
|
||||||
elem = Popen(('./zint','--directsvg','--notext', '-d', items[1]), stdout = PIPE).communicate()[0].split('\n')
|
|
||||||
elem = elem[8:-2]
|
|
||||||
elem[0] = elem[0].replace('id="barcode"', 'transform="matrix(%f,0,0,%f,%f,%f)"' % (scalex, scaley, 50+i*140 , 180+j*140) )
|
|
||||||
elem.insert(-1, ' <text x="39.50" y="69.00" text-anchor="middle" font-family="Helvetica" font-size="14.0" fill="#000000" >%s</text>' % items[0])
|
|
||||||
f.write('\n'.join(elem)+'\n\n')
|
|
||||||
i += 1
|
|
||||||
if i >= width:
|
|
||||||
i = 0
|
|
||||||
j += 1
|
|
||||||
|
|
||||||
if not f.closed:
|
|
||||||
f.write(svgfoot)
|
|
||||||
f.close()
|
|
|
@ -1,9 +0,0 @@
|
||||||
on brmbar:
|
|
||||||
select distinct barcode from barcodes b, transactions t, accounts a where t.responsible=a.id and time>'2015-01-01' and b.account=a.id order by barcode asc;
|
|
||||||
|
|
||||||
run this locally and paste output of previous command:
|
|
||||||
|
|
||||||
while read tmp; do echo "$tmp;$tmp";done|grep -v overflow|python2 ./barcode-generator.py
|
|
||||||
|
|
||||||
print resulting SVG files
|
|
||||||
|
|
2
brmbar3/.gitignore
vendored
2
brmbar3/.gitignore
vendored
|
@ -1,3 +1 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
*.log
|
|
||||||
brmbar/*.pyc
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
How to "reset" the database - drop all history and keep only accounts with non-zero balance.
|
|
||||||
|
|
||||||
Legend:
|
|
||||||
> - SQL commands
|
|
||||||
$ - shell commands
|
|
||||||
|
|
||||||
Run the (full) inventory.
|
|
||||||
|
|
||||||
Get number of the first inventory TX.
|
|
||||||
|
|
||||||
> select id from account_balances where id in (select id from accounts where currency not in (select distinct currency from
|
|
||||||
transaction_nicesplits where transaction >= NUMBER_HERE and currency != 1 and memo like '%Inventory fix%') and acctype = 'inventory') and crbalance != 0 \g 'vynulovat'
|
|
||||||
$ ./brmbar-cli.py inventory `cat vynulovat | while read x; do echo $x 0; done`
|
|
||||||
|
|
||||||
Backup the database
|
|
||||||
$ pg_dump brmbar > backup.sql
|
|
||||||
|
|
||||||
Dump "> SELECT * FROM account_balances;" to file N.
|
|
||||||
|
|
||||||
Dump inventory to file nastavit FIXME.
|
|
||||||
|
|
||||||
Drop all transactions:
|
|
||||||
> delete from transaction_splits;
|
|
||||||
> delete from transactions;
|
|
||||||
|
|
||||||
Restore inventory:
|
|
||||||
$ cat nastavit | while read acc p amt; do ./brmbar-cli.py inventory $acc `echo $amt | grep -oE "^[0-9-]+"`; done
|
|
||||||
|
|
||||||
Restore cash balance:
|
|
||||||
$ cat N | grep debt | tr -s " " |cut -d \| -f 2,4 | while read acc p amt; do ./brmbar-cli.py changecredit $acc `echo $amt | grep -oE "^[0-9-]+"`; done
|
|
||||||
|
|
||||||
Delete zero-balance accounts:
|
|
||||||
> delete from accounts where accounts.id not in (select id from account_balances);
|
|
||||||
|
|
||||||
Delete orphaned barcodes:
|
|
||||||
> delete from barcodes where barcodes.account not in (select id from account_balances);
|
|
||||||
|
|
||||||
Delete orphaned currencies and exchange rates:
|
|
||||||
> CREATE OR REPLACE VIEW "a_tmp" AS
|
|
||||||
SELECT ts.account AS id, accounts.name, accounts.acctype, accounts.currency AS fff, (- sum(CASE WHEN (ts.side = 'credit'::transaction_split_side) THEN (- ts.amount) ELSE ts.amount END)) AS crbalance FROM (transaction_splits ts LEFT JOIN accounts ON ((accounts.id = ts.account))) GROUP BY ts.account, accounts.name, accounts.id, accounts.acctype ORDER BY (- sum(CASE WHEN (ts.side = 'credit'::transaction_split_side) THEN (- ts.amount) ELSE ts.amount END));
|
|
||||||
|
|
||||||
> delete from exchange_rates where source not in (select fff from a_tmp);
|
|
||||||
> delete from currencies where id not in (select fff from a_tmp);
|
|
||||||
|
|
||||||
> DROP VIEW "a_tmp";
|
|
||||||
|
|
||||||
Drop obsolete exchange rates:
|
|
||||||
|
|
||||||
> delete from exchange_rates where
|
|
||||||
valid_since <> (SELECT max(valid_since)
|
|
||||||
FROM exchange_rates e
|
|
||||||
WHERE e.target = exchange_rates.target and e.source = exchange_rates.source)
|
|
||||||
|
|
||||||
Restore system accounts:
|
|
||||||
> INSERT INTO "accounts" ("name", "currency", "acctype", "active")
|
|
||||||
VALUES ('BrmBar Profits', '1', 'income', '1');
|
|
||||||
> INSERT INTO "accounts" ("name", "currency", "acctype", "active")
|
|
||||||
VALUES ('BrmBar Excess', '1', 'income', '1');
|
|
||||||
> INSERT INTO "accounts" ("name", "currency", "acctype", "active")
|
|
||||||
VALUES ('BrmBar Deficit', '1', 'expense', '1');
|
|
||||||
> INSERT INTO "accounts" ("name", "currency", "acctype", "active")
|
|
||||||
VALUES ('BrmBar Cash', '1', 'cash', '1');
|
|
||||||
|
|
||||||
Restart brmbar.
|
|
29
brmbar3/SQL
29
brmbar3/SQL
|
@ -1,11 +1,11 @@
|
||||||
CREATE SEQUENCE currencies_id_seq START WITH 2 INCREMENT BY 1;
|
CREATE SEQUENCE currencies_id_seq START WITH 1 INCREMENT BY 1;
|
||||||
CREATE TABLE currencies (
|
CREATE TABLE currencies (
|
||||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('currencies_id_seq'::regclass),
|
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('currencies_id_seq'::regclass),
|
||||||
name VARCHAR(128) NOT NULL,
|
name VARCHAR(128) NOT NULL,
|
||||||
UNIQUE(name)
|
UNIQUE(name)
|
||||||
);
|
);
|
||||||
-- Some code depends on the primary physical currency to have id 1.
|
-- Some code depends on the primary physical currency to have id 1.
|
||||||
INSERT INTO currencies (id, name) VALUES (1, 'Kč');
|
INSERT INTO currencies (name) VALUES ('Kč');
|
||||||
|
|
||||||
CREATE TYPE exchange_rate_direction AS ENUM ('source_to_target', 'target_to_source');
|
CREATE TYPE exchange_rate_direction AS ENUM ('source_to_target', 'target_to_source');
|
||||||
CREATE TABLE exchange_rates (
|
CREATE TABLE exchange_rates (
|
||||||
|
@ -39,7 +39,8 @@ CREATE TABLE accounts (
|
||||||
|
|
||||||
acctype account_type NOT NULL,
|
acctype account_type NOT NULL,
|
||||||
|
|
||||||
active BOOLEAN NOT NULL DEFAULT TRUE
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
crbalance DECIMAL(12,2) NOT NULL
|
||||||
);
|
);
|
||||||
INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Cash', (SELECT id FROM currencies WHERE name='Kč'), 'cash');
|
INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Cash', (SELECT id FROM currencies WHERE name='Kč'), 'cash');
|
||||||
INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Profits', (SELECT id FROM currencies WHERE name='Kč'), 'income');
|
INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Profits', (SELECT id FROM currencies WHERE name='Kč'), 'income');
|
||||||
|
@ -92,12 +93,14 @@ CREATE TABLE transaction_splits (
|
||||||
-- Note that currency information is currently not supplied; inventory items
|
-- Note that currency information is currently not supplied; inventory items
|
||||||
-- have balances in stock amounts.
|
-- have balances in stock amounts.
|
||||||
CREATE VIEW account_balances AS
|
CREATE VIEW account_balances AS
|
||||||
SELECT ts.account AS id, accounts.name AS name, accounts.acctype AS acctype,
|
SELECT id, name, acctype, crbalance FROM accounts ORDER BY crbalance ASC;
|
||||||
-SUM(CASE WHEN ts.side = 'credit' THEN -ts.amount ELSE ts.amount END) AS crbalance
|
|
||||||
FROM transaction_splits AS ts
|
-- SELECT ts.account AS id, accounts.name AS name, accounts.acctype AS acctype,
|
||||||
LEFT JOIN accounts ON accounts.id = ts.account
|
-- -SUM(CASE WHEN ts.side = 'credit' THEN -ts.amount ELSE ts.amount END) AS crbalance
|
||||||
GROUP BY ts.account, accounts.name, accounts.acctype
|
-- FROM transaction_splits AS ts
|
||||||
ORDER BY crbalance ASC;
|
-- LEFT JOIN accounts ON accounts.id = ts.account
|
||||||
|
-- GROUP BY ts.account, accounts.name, accounts.acctype
|
||||||
|
-- ORDER BY crbalance ASC;
|
||||||
|
|
||||||
-- Transaction splits in a form that's nicer to query during manual inspection
|
-- Transaction splits in a form that's nicer to query during manual inspection
|
||||||
CREATE VIEW transaction_nicesplits AS
|
CREATE VIEW transaction_nicesplits AS
|
||||||
|
@ -107,9 +110,10 @@ CREATE VIEW transaction_nicesplits AS
|
||||||
FROM transaction_splits AS ts LEFT JOIN accounts AS a ON a.id = ts.account
|
FROM transaction_splits AS ts LEFT JOIN accounts AS a ON a.id = ts.account
|
||||||
ORDER BY ts.id;
|
ORDER BY ts.id;
|
||||||
|
|
||||||
-- List transactions with summary information regarding their cash element.
|
-- List transactions with summary information regarding their cash element
|
||||||
|
-- (except in case of transfers between cash and debt accounts, which will cancel out).
|
||||||
CREATE VIEW transaction_cashsums AS
|
CREATE VIEW transaction_cashsums AS
|
||||||
SELECT t.id AS id, t.time AS time, SUM(credit_cash) AS cash_credit, SUM(debit_cash) AS cash_debit, a.name AS responsible, t.description AS description
|
SELECT t.id AS id, t.time AS time, SUM(credit_cash) AS cash_credit, SUM(debit_cash) AS cash_debit, t.description AS description
|
||||||
FROM transactions AS t
|
FROM transactions AS t
|
||||||
LEFT JOIN (SELECT cts.amount AS credit_cash, cts.transaction AS cts_t
|
LEFT JOIN (SELECT cts.amount AS credit_cash, cts.transaction AS cts_t
|
||||||
FROM transaction_nicesplits AS cts
|
FROM transaction_nicesplits AS cts
|
||||||
|
@ -123,5 +127,4 @@ CREATE VIEW transaction_cashsums AS
|
||||||
WHERE a.currency = (SELECT currency FROM accounts WHERE name = 'BrmBar Cash')
|
WHERE a.currency = (SELECT currency FROM accounts WHERE name = 'BrmBar Cash')
|
||||||
AND a.acctype IN ('cash', 'debt')
|
AND a.acctype IN ('cash', 'debt')
|
||||||
AND dts.amount > 0) debit ON dts_t = t.id
|
AND dts.amount > 0) debit ON dts_t = t.id
|
||||||
LEFT JOIN accounts AS a ON a.id = t.responsible
|
GROUP BY t.id ORDER BY t.id;
|
||||||
GROUP BY t.id, a.name ORDER BY t.id DESC;
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
CREATE OR REPLACE FUNCTION accounts_id_seq_value()
|
|
||||||
RETURNS bigint
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
SECURITY DEFINER
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
result bigint;
|
|
||||||
BEGIN
|
|
||||||
SELECT last_value FROM accounts_id_seq
|
|
||||||
INTO result;
|
|
||||||
RETURN result;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION transactions_id_seq_value()
|
|
||||||
RETURNS bigint
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
SECURITY DEFINER
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
result bigint;
|
|
||||||
BEGIN
|
|
||||||
SELECT last_value FROM transactions_id_seq
|
|
||||||
INTO result;
|
|
||||||
RETURN result;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION transaction_splits_id_seq_value()
|
|
||||||
RETURNS bigint
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
SECURITY DEFINER
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
result bigint;
|
|
||||||
BEGIN
|
|
||||||
SELECT last_value FROM transaction_splits_id_seq
|
|
||||||
INTO result;
|
|
||||||
RETURN result;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION currencies_id_seq_value()
|
|
||||||
RETURNS bigint
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
SECURITY DEFINER
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
result bigint;
|
|
||||||
BEGIN
|
|
||||||
SELECT last_value FROM currencies_id_seq
|
|
||||||
INTO result;
|
|
||||||
RETURN result;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
--RESET search_path;
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
-- intoduce implementation schema
|
|
||||||
CREATE SCHEMA IF NOT EXISTS brmbar_implementation;
|
|
||||||
-- version table (with initialization)
|
|
||||||
CREATE TABLE IF NOT EXISTS brmbar_implementation.brmbar_schema (
|
|
||||||
ver INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
DO $$
|
|
||||||
DECLARE v INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT ver FROM brmbar_implementation.brmbar_schema INTO v;
|
|
||||||
IF v IS NULL THEN
|
|
||||||
INSERT INTO brmbar_implementation.brmbar_schema (ver) VALUES (1);
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_implementation.has_exact_schema_version(
|
|
||||||
IN i_ver INTEGER NOT NULL
|
|
||||||
) RETURNS INTEGER
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
v_ver INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT ver INTO STRICT v_ver FROM brmbar_implementation.brmbar_schema;
|
|
||||||
IF v_ver IS NULL or v_ver <> i_ver THEN
|
|
||||||
RAISE EXCEPTION 'Invalid brmbar schema version';
|
|
||||||
END IF;
|
|
||||||
RETURN v_ver;
|
|
||||||
/*
|
|
||||||
EXCEPTION
|
|
||||||
WHEN NO_DATA_FOUND THEN
|
|
||||||
RAISE EXCEPTION 'PID % not found';
|
|
||||||
WHEN TOO_MANY_ROWS THEN
|
|
||||||
RAISE EXCEPTION 'PID % not unique';
|
|
||||||
*/
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_implementation.upgrade_schema_version_to(
|
|
||||||
IN i_ver INTEGER NOT NULL
|
|
||||||
) RETURNS INTEGER
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
v_ver INTEGER;
|
|
||||||
BEGIN
|
|
||||||
SELECT brmbar_implementation.has_exact_schema_version(i_ver) INTO v_ver;
|
|
||||||
IF v_ver + 1 = i_ver THEN
|
|
||||||
UPDATE brmbar_implementation.brmbar_schema SET ver = i_ver;
|
|
||||||
ELSE
|
|
||||||
RAISE EXCEPTION 'Invalid brmbar schema version';
|
|
||||||
END IF;
|
|
||||||
RETURN i_ver;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
--RESET search_path
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
--- upgrade schema
|
|
||||||
DO $upgrade_block$
|
|
||||||
DECLARE
|
|
||||||
current_ver INTEGER;
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
-- confirm that we are upgrading from version 1
|
|
||||||
SELECT brmbar_implementation.has_exact_schema_version(1) INTO current_ver;
|
|
||||||
IF current_ver <> 1 THEN
|
|
||||||
RAISE EXCEPTION 'BrmBar schema version % cannot be upgraded to version 2.', current_ver;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
-- structural changes
|
|
||||||
|
|
||||||
-- TRADING ACCOUNTS
|
|
||||||
--START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
||||||
|
|
||||||
-- currency trading accounts - account type
|
|
||||||
ALTER TYPE public.account_type ADD VALUE IF NOT EXISTS 'trading';
|
|
||||||
|
|
||||||
-- constraint needed for foreign key in currencies table
|
|
||||||
ALTER TABLE public.accounts ADD CONSTRAINT accounts_id_acctype_key UNIQUE(id, acctype);
|
|
||||||
|
|
||||||
-- add columns to currencies to record the trading account associated with the currency
|
|
||||||
ALTER TABLE public.currencies
|
|
||||||
ADD COLUMN IF NOT EXISTS trading_account integer,
|
|
||||||
ADD COLUMN IF NOT EXISTS trading_account_type account_type GENERATED ALWAYS AS ('trading'::public.account_type) STORED;
|
|
||||||
|
|
||||||
-- make trading accounts (without making duplicates)
|
|
||||||
INSERT INTO public.accounts ("name", "currency", acctype)
|
|
||||||
SELECT
|
|
||||||
'Currency Trading Account: ' || c."name",
|
|
||||||
c.id,
|
|
||||||
'trading'::public.account_type
|
|
||||||
FROM public.currencies AS c
|
|
||||||
WHERE NOT EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM public.accounts a
|
|
||||||
WHERE a.currency = c.id AND a.acctype = 'trading'::public.account_type
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
-- record the trading account IDs in currencies table
|
|
||||||
UPDATE public.currencies AS c SET (trading_account) = (SELECT a.id FROM public.accounts AS a WHERE a.currency = c.id AND c.acctype = 'trading'::public.account_type);
|
|
||||||
|
|
||||||
-- foreign key to check the validity of currency trading account reference
|
|
||||||
ALTER TABLE public.currencies
|
|
||||||
ADD CONSTRAINT currencies_trading_fkey FOREIGN KEY (trading_account, trading_account_type)
|
|
||||||
REFERENCES xaccounts(id,acctype) DEFERRABLE INITIALLY DEFERRED;
|
|
||||||
|
|
||||||
--COMMIT AND CHAIN;
|
|
||||||
|
|
||||||
SELECT brmbar_implementation.upgrade_schema_version_to(2) INTO current_ver;
|
|
||||||
-- end of upgrade do block
|
|
||||||
end
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -68,53 +68,12 @@ Administrative Usage
|
||||||
account (and so will typing "joehacker" on a physical keyboard).
|
account (and so will typing "joehacker" on a physical keyboard).
|
||||||
|
|
||||||
* If your inventory stock count or cash box amount does not match
|
* If your inventory stock count or cash box amount does not match
|
||||||
the in-system data, you will need to make a corrective transaction.
|
the in-system data, you will need to make a corrective transaction.
|
||||||
To fix cash amount to reality in which you counted 1234Kč, use
|
In the future, brmbar-cli.py will support this, but there is no
|
||||||
|
implementation yet; it's not entirely clear yet what is the proper
|
||||||
./brmbar-cli.py fixcash 1234
|
way to do this from the accounting standpoint. In the meantime, you
|
||||||
|
can use SQL INSERTs to manually create a transaction with appropriate
|
||||||
whereas to fix amount of a particular stock, use
|
transaction splits (see doc/architecture for details on splits).
|
||||||
|
|
||||||
./brmbar-cli.py inventory-interactive
|
|
||||||
|
|
||||||
then scan the item barcode and then enter the right amount.
|
|
||||||
|
|
||||||
* If you want to view recent transactions, run
|
|
||||||
|
|
||||||
psql brmbar
|
|
||||||
select * from transaction_cashsums;
|
|
||||||
|
|
||||||
* If you want to undo a transaction, get its id (using the select above)
|
|
||||||
and run
|
|
||||||
|
|
||||||
./brmbar-cli.py undo ID
|
|
||||||
|
|
||||||
* If you want to get overview of the financial situation, run
|
|
||||||
|
|
||||||
./brmbar-cli.py stats
|
|
||||||
|
|
||||||
The following items represent "material", "tangible" assets:
|
|
||||||
|
|
||||||
* Cash - how much should be in the money box
|
|
||||||
* Overflow - how much cash is stored in overflow credit accounts (pockets of admins)
|
|
||||||
* Inventory - how much worth (buy price) is the current inventory stock
|
|
||||||
|
|
||||||
I.e., cash plus overflow plus inventory is how much brmbar is worth
|
|
||||||
and cash plus overflow is how much brmbar can spend right now.
|
|
||||||
|
|
||||||
The following items represent "virtual" accounts which determine
|
|
||||||
the logical composition of the assets:
|
|
||||||
|
|
||||||
* Credit - sum of all credit accounts, i.e. money stored in brmbar by its users;
|
|
||||||
i.e. how much of the assets is users' money
|
|
||||||
* Profit - accumulated profit made by brmbar on buy/sell margins (but receipts
|
|
||||||
and inventory deficits are subtracted); i.e. how much of the assets is brmbar's
|
|
||||||
own money
|
|
||||||
* Fixups - sum of gains and losses accrued by inventory fixups, i.e. stemming
|
|
||||||
from differences between accounting and reality - positive is good, negative
|
|
||||||
is bad; this amount is added to profit on consolidation
|
|
||||||
|
|
||||||
The total worth of the material and virtual accounts should be equal.
|
|
||||||
|
|
||||||
|
|
||||||
Useful SQL queries
|
Useful SQL queries
|
||||||
|
@ -122,32 +81,32 @@ Useful SQL queries
|
||||||
|
|
||||||
* Compute sum of sold stock:
|
* Compute sum of sold stock:
|
||||||
|
|
||||||
select sum(amount) from transactions
|
select sum(amount) from transactions
|
||||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||||
where description like '% sale %' and side = 'debit';
|
where description like '% sale %' and side = 'debit';
|
||||||
|
|
||||||
* List of items not covered by inventory check:
|
* List of items not covered by inventory check:
|
||||||
|
|
||||||
select * from account_balances
|
select * from account_balances
|
||||||
where id not in (select account from transactions
|
where id not in (select account from transactions
|
||||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||||
where description like '% inventory %')
|
where description like '% inventory %')
|
||||||
and acctype = 'inventory';
|
and acctype = 'inventory';
|
||||||
|
|
||||||
* List all cash transactions:
|
* List all cash transactions:
|
||||||
|
|
||||||
select time, transactions.id, description, responsible, amount from transactions
|
select time, transactions.id, description, responsible, amount from transactions
|
||||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||||
where transaction_splits.account = 1;
|
where transaction_splits.account = 1;
|
||||||
|
|
||||||
* List all inventory items ordered by their cummulative worth:
|
* List all inventory items ordered by their cummulative worth:
|
||||||
|
|
||||||
select foo.*, foo.rate * -foo.crbalance as worth from
|
select foo.*, foo.rate * -foo.crbalance as worth from
|
||||||
(select account_balances.*,
|
(select account_balances.*,
|
||||||
(select exchange_rates.rate from exchange_rates, accounts
|
(select exchange_rates.rate from exchange_rates, accounts
|
||||||
where exchange_rates.target = accounts.currency
|
where exchange_rates.target = accounts.currency
|
||||||
and accounts.id = account_balances.id
|
and accounts.id = account_balances.id
|
||||||
order by exchange_rates.valid_since limit 1) as rate
|
order by exchange_rates.valid_since limit 1) as rate
|
||||||
from account_balances where account_balances.acctype = 'inventory')
|
from account_balances where account_balances.acctype = 'inventory')
|
||||||
as foo order by worth;
|
as foo order by worth;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
Accounts with multiple barcodes:
|
|
||||||
|
|
||||||
SELECT accounts.name,barcodes.account,barcodes.barcode
|
|
||||||
FROM "barcodes"
|
|
||||||
join accounts on accounts.id = barcodes.account
|
|
||||||
where barcodes.account in (select a from (select count(*) as c, account as a from barcodes group by account) as dt where c > 1)
|
|
||||||
ORDER BY "account" DESC
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
case $1 in
|
|
||||||
alert) mplayer -really-quiet ~/trombone.wav & ;;
|
|
||||||
limit) mplayer -really-quiet ~/much.wav & ;;
|
|
||||||
charge) mplayer -really-quiet ~/charge.wav & ;;
|
|
||||||
esac
|
|
|
@ -1,42 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import brmbar
|
|
||||||
import math
|
|
||||||
from brmbar import Database
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(usage = "File format: EAN amount total_price name, e.g. 4001242002377 6 167.40 Chio Tortillas")
|
|
||||||
parser.add_argument("filename")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
db = Database.Database("dbname=brmbar")
|
|
||||||
shop = brmbar.Shop.new_with_defaults(db)
|
|
||||||
currency = shop.currency
|
|
||||||
|
|
||||||
# ...
|
|
||||||
total = 0
|
|
||||||
with open(args.filename) as fin:
|
|
||||||
for line in fin:
|
|
||||||
split = line.split(" ")
|
|
||||||
ean, amount, price_total, name = split[0], int(split[1]), float(split[2]), " ".join(split[3:])
|
|
||||||
name = name.strip()
|
|
||||||
|
|
||||||
price_buy = price_total / amount
|
|
||||||
acct = brmbar.Account.load_by_barcode(db, ean)
|
|
||||||
if not acct:
|
|
||||||
print("Creating account for EAN {} '{}'".format(ean, name))
|
|
||||||
invcurr = brmbar.Currency.create(db, name)
|
|
||||||
acct = brmbar.Account.create(db, name, invcurr, "inventory")
|
|
||||||
acct.add_barcode(ean)
|
|
||||||
price_sell = max(math.ceil(price_buy * 1.15), price_buy)
|
|
||||||
acct.currency.update_sell_rate(currency, price_sell)
|
|
||||||
acct.currency.update_buy_rate(currency, price_buy)
|
|
||||||
cash = shop.buy_for_cash(acct, amount)
|
|
||||||
total += cash
|
|
||||||
print("Increased by {}, take {} from cashbox".format(amount, cash))
|
|
||||||
print("Total is {}".format(total))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
|
@ -15,11 +15,9 @@ Usage: brmbar-cli.py COMMAND ARGS...
|
||||||
1. Commands pertaining the standard operation
|
1. Commands pertaining the standard operation
|
||||||
showcredit USER
|
showcredit USER
|
||||||
changecredit USER +-AMT
|
changecredit USER +-AMT
|
||||||
sellitem {USER|"cash"} ITEM +-AMT
|
sellitem USER ITEM +-AMT
|
||||||
You can use negative AMT to undo a sale.
|
You can use negative AMT to undo a sale.
|
||||||
restock ITEM AMT
|
|
||||||
userinfo USER
|
userinfo USER
|
||||||
userlog USER TIMESTAMP
|
|
||||||
iteminfo ITEM
|
iteminfo ITEM
|
||||||
|
|
||||||
2. Management commands
|
2. Management commands
|
||||||
|
@ -32,21 +30,15 @@ Usage: brmbar-cli.py COMMAND ARGS...
|
||||||
screen of the GUI.
|
screen of the GUI.
|
||||||
adduser USER
|
adduser USER
|
||||||
Add user (debt) account with given username.
|
Add user (debt) account with given username.
|
||||||
undo TRANSID
|
inventory ITEM1 NEW_AMOUNT1 ITEM2 NEW_AMOUNT2
|
||||||
Commit a transaction that reverses all splits of a transaction with
|
Inventory recounting (fixing the number of items)
|
||||||
a given id (to find out that id: select * from transaction_cashsums;)
|
inventory-interactive
|
||||||
|
Launches interactive mode for performing inventory with barcode reader
|
||||||
3. Inventorization
|
changecash AMT
|
||||||
|
Fixes the cash and puts money difference into excess or deficit account
|
||||||
inventory ITEM1 NEW_AMOUNT1 ITEM2 NEW_AMOUNT2
|
consolidate
|
||||||
Inventory recounting (fixing the number of items)
|
Wraps up inventory + cash recounting, transferring the excess and
|
||||||
inventory-interactive
|
deficit accounts balance to the profits account and resetting them
|
||||||
Launches interactive mode for performing inventory with barcode reader
|
|
||||||
fixcash AMT
|
|
||||||
Fixes the cash and puts money difference into excess or deficit account
|
|
||||||
consolidate
|
|
||||||
Wraps up inventory + cash recounting, transferring the excess and
|
|
||||||
deficit accounts balance to the profits account and resetting them
|
|
||||||
|
|
||||||
USER and ITEM may be barcodes or account ids. AMT may be
|
USER and ITEM may be barcodes or account ids. AMT may be
|
||||||
both positive and negative amount (big difference to other
|
both positive and negative amount (big difference to other
|
||||||
|
@ -56,19 +48,7 @@ For users, you can use their name as USER as their username
|
||||||
is also the barcode. For items, use listitems command first
|
is also the barcode. For items, use listitems command first
|
||||||
to find out the item id.
|
to find out the item id.
|
||||||
|
|
||||||
EXAMPLES:
|
Commands prefixed with ! are not implemented yet.""")
|
||||||
|
|
||||||
Transfer 35Kc from pasky to sachy:
|
|
||||||
|
|
||||||
$ ./brmbar-cli.py changecredit pasky -35
|
|
||||||
$ ./brmbar-cli.py changecredit sachy +35
|
|
||||||
|
|
||||||
Buy one RaspberryPi for cash from commandline:
|
|
||||||
|
|
||||||
$ ./brmbar-cli.py listitems | grep -i raspberry
|
|
||||||
Raspberry Pi 2 1277 1.00 pcs
|
|
||||||
$ ./brmbar-cli.py sellitem cash 1277 1
|
|
||||||
""")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,12 +77,6 @@ def load_item(inp):
|
||||||
exit(1)
|
exit(1)
|
||||||
return acct
|
return acct
|
||||||
|
|
||||||
def load_item_by_barcode(inp):
|
|
||||||
acct = brmbar.Account.load_by_barcode(db, inp)
|
|
||||||
if acct.acctype != "inventory":
|
|
||||||
print("Bad EAN " + inp + " type " + acct.acctype, file=sys.stderr)
|
|
||||||
exit(1)
|
|
||||||
return acct
|
|
||||||
|
|
||||||
db = Database.Database("dbname=brmbar")
|
db = Database.Database("dbname=brmbar")
|
||||||
shop = brmbar.Shop.new_with_defaults(db)
|
shop = brmbar.Shop.new_with_defaults(db)
|
||||||
|
@ -126,20 +100,14 @@ elif sys.argv[1] == "changecredit":
|
||||||
print("{}: {}".format(acct.name, acct.negbalance_str()))
|
print("{}: {}".format(acct.name, acct.negbalance_str()))
|
||||||
|
|
||||||
elif sys.argv[1] == "sellitem":
|
elif sys.argv[1] == "sellitem":
|
||||||
if sys.argv[2] == "cash":
|
uacct = load_user(sys.argv[2])
|
||||||
uacct = shop.cash
|
|
||||||
else:
|
|
||||||
uacct = load_user(sys.argv[2])
|
|
||||||
iacct = load_item(sys.argv[3])
|
iacct = load_item(sys.argv[3])
|
||||||
amt = int(sys.argv[4])
|
amt = int(sys.argv[4])
|
||||||
if amt > 0:
|
if amt > 0:
|
||||||
if uacct == shop.cash:
|
shop.sell(item = iacct, user = uacct, amount = amt)
|
||||||
shop.sell_for_cash(item = iacct, amount = amt)
|
|
||||||
else:
|
|
||||||
shop.sell(item = iacct, user = uacct, amount = amt)
|
|
||||||
elif amt < 0:
|
elif amt < 0:
|
||||||
shop.undo_sale(item = iacct, user = uacct, amount = -amt)
|
shop.undo_sale(item = iacct, user = uacct, amount = -amt)
|
||||||
print("{}: {}".format(uacct.name, uacct.balance_str() if uacct == shop.cash else uacct.negbalance_str()))
|
print("{}: {}".format(uacct.name, uacct.negbalance_str()))
|
||||||
print("{}: {}".format(iacct.name, iacct.balance_str()))
|
print("{}: {}".format(iacct.name, iacct.balance_str()))
|
||||||
|
|
||||||
elif sys.argv[1] == "userinfo":
|
elif sys.argv[1] == "userinfo":
|
||||||
|
@ -149,14 +117,6 @@ elif sys.argv[1] == "userinfo":
|
||||||
res = db.execute_and_fetchall("SELECT barcode FROM barcodes WHERE account = %s", [acct.id])
|
res = db.execute_and_fetchall("SELECT barcode FROM barcodes WHERE account = %s", [acct.id])
|
||||||
print("Barcodes: " + ", ".join(map((lambda r: r[0]), res)))
|
print("Barcodes: " + ", ".join(map((lambda r: r[0]), res)))
|
||||||
|
|
||||||
elif sys.argv[1] == "userlog":
|
|
||||||
acct = load_user(sys.argv[2])
|
|
||||||
timestamp = sys.argv[3]
|
|
||||||
|
|
||||||
res = db.execute_and_fetchall("SELECT * FROM transaction_cashsums WHERE responsible=%s and time > TIMESTAMP %s ORDER BY time", [acct.name,timestamp])
|
|
||||||
for transaction in res:
|
|
||||||
print('\t'.join([str(f) for f in transaction]))
|
|
||||||
|
|
||||||
elif sys.argv[1] == "iteminfo":
|
elif sys.argv[1] == "iteminfo":
|
||||||
acct = load_item(sys.argv[2])
|
acct = load_item(sys.argv[2])
|
||||||
print("{} (id {}): {} pcs".format(acct.name, acct.id, acct.balance()))
|
print("{} (id {}): {} pcs".format(acct.name, acct.id, acct.balance()))
|
||||||
|
@ -176,27 +136,18 @@ elif sys.argv[1] == "listitems":
|
||||||
print("{}\t{}\t{} pcs".format(acct.name, acct.id, acct.balance()))
|
print("{}\t{}\t{} pcs".format(acct.name, acct.id, acct.balance()))
|
||||||
|
|
||||||
elif sys.argv[1] == "stats":
|
elif sys.argv[1] == "stats":
|
||||||
print("--- Material Assets ---")
|
|
||||||
print("Cash: {}".format(shop.cash.balance_str()))
|
print("Cash: {}".format(shop.cash.balance_str()))
|
||||||
print("Overflow: {}".format(shop.currency.str(shop.credit_balance(overflow='only'))))
|
|
||||||
print("Inventory: {}".format(shop.inventory_balance_str()))
|
|
||||||
print("--- Logical Accounts ---")
|
|
||||||
print("Credit: {}".format(shop.credit_negbalance_str(overflow='exclude')))
|
|
||||||
print("Profit: {}".format(shop.profits.balance_str()))
|
print("Profit: {}".format(shop.profits.balance_str()))
|
||||||
print("Fixups: {} (excess {}, deficit {})".format(
|
print("Credit: {}".format(shop.credit_negbalance_str()))
|
||||||
-shop.excess.balance() - shop.deficit.balance(),
|
print("Inventory: {}".format(shop.inventory_balance_str()))
|
||||||
shop.excess.negbalance_str(),
|
print("Excess: {}".format(shop.excess.negbalance_str()))
|
||||||
shop.deficit.balance_str()))
|
print("Deficit: {}".format(shop.deficit.balance_str()))
|
||||||
|
|
||||||
elif sys.argv[1] == "adduser":
|
elif sys.argv[1] == "adduser":
|
||||||
acct = brmbar.Account.create(db, sys.argv[2], brmbar.Currency.load(db, id = 1), 'debt')
|
acct = brmbar.Account.create(db, sys.argv[2], brmbar.Currency.load(db, id = 1), 'debt')
|
||||||
acct.add_barcode(sys.argv[2]) # will commit
|
acct.add_barcode(sys.argv[2]) # will commit
|
||||||
print("{}: id {}".format(acct.name, acct.id));
|
print("{}: id {}".format(acct.name, acct.id));
|
||||||
|
|
||||||
elif sys.argv[1] == "undo":
|
|
||||||
newtid = shop.undo(int(sys.argv[2]))
|
|
||||||
print("Transaction %d undone by reverse transaction %d" % (int(sys.argv[2]), newtid))
|
|
||||||
|
|
||||||
elif sys.argv[1] == "inventory":
|
elif sys.argv[1] == "inventory":
|
||||||
if (len(sys.argv) % 2 != 0 or len(sys.argv) < 4):
|
if (len(sys.argv) % 2 != 0 or len(sys.argv) < 4):
|
||||||
print ("Invalid number of parameters, count your parameters.")
|
print ("Invalid number of parameters, count your parameters.")
|
||||||
|
@ -214,27 +165,26 @@ elif sys.argv[1] == "inventory":
|
||||||
elif sys.argv[1] == "inventory-interactive":
|
elif sys.argv[1] == "inventory-interactive":
|
||||||
print("Inventory interactive mode. To exit interactive mode just enter empty barcode")
|
print("Inventory interactive mode. To exit interactive mode just enter empty barcode")
|
||||||
|
|
||||||
while True:
|
keep_entering = True
|
||||||
|
while keep_entering:
|
||||||
barcode = str(input("Enter barcode:"))
|
barcode = str(input("Enter barcode:"))
|
||||||
fuckyou = input("fuckyou")
|
fuckyou = input("fuckyou")
|
||||||
if barcode == "":
|
if barcode == "":
|
||||||
break
|
break
|
||||||
iacct = brmbar.Account.load_by_barcode(db, barcode)
|
|
||||||
amount = str(input("What is the amount of {} in reality (expected: {} pcs):".format(iacct.name, iacct.balance())))
|
|
||||||
if amount == "":
|
|
||||||
break
|
|
||||||
elif int(amount) > 10000:
|
|
||||||
print("Ignoring too high amount {}, assuming barcode was mistakenly scanned instead".format(amount))
|
|
||||||
else:
|
else:
|
||||||
iamt = int(amount)
|
iacct = brmbar.Account.load_by_barcode(db, barcode)
|
||||||
print("Current state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
|
amount = str(input("What is the amount of {} in reality current is {}:".format(iacct.name, iacct.balance())))
|
||||||
if shop.fix_inventory(item = iacct, amount = iamt):
|
if amount == "":
|
||||||
print("New state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
|
break
|
||||||
else:
|
else:
|
||||||
print("No action needed, amount is correct.")
|
iamt = int(amount)
|
||||||
|
print("Current state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
|
||||||
|
if shop.fix_inventory(item = iacct, amount = iamt):
|
||||||
|
print("New state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
|
||||||
|
else:
|
||||||
|
print ("No action needed amount is correct.")
|
||||||
print("End of processing. Bye")
|
print("End of processing. Bye")
|
||||||
|
elif sys.argv[1] == "changecash":
|
||||||
elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash":
|
|
||||||
if (len(sys.argv) != 3):
|
if (len(sys.argv) != 3):
|
||||||
print ("Invalid number of parameters, check your parameters.")
|
print ("Invalid number of parameters, check your parameters.")
|
||||||
else:
|
else:
|
||||||
|
@ -244,23 +194,12 @@ elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash":
|
||||||
print("New Cash is : {}".format(shop.cash.balance_str()))
|
print("New Cash is : {}".format(shop.cash.balance_str()))
|
||||||
else:
|
else:
|
||||||
print ("No action needed amount is the same.")
|
print ("No action needed amount is the same.")
|
||||||
|
|
||||||
elif sys.argv[1] == "consolidate":
|
elif sys.argv[1] == "consolidate":
|
||||||
if (len(sys.argv) != 2):
|
if (len(sys.argv) != 2):
|
||||||
print ("Invalid number of parameters, check your parameters.")
|
print ("Invalid number of parameters, check your parameters.")
|
||||||
else:
|
else:
|
||||||
shop.consolidate()
|
shop.consolidate()
|
||||||
|
|
||||||
elif sys.argv[1] in {"restock", "restock_ean"}:
|
|
||||||
if (len(sys.argv) != 4):
|
|
||||||
print ("Invalid number of parameters, check your parameters.")
|
|
||||||
else:
|
|
||||||
iacct = (load_item if sys.argv[1] == "restock" else load_item_by_barcode)(sys.argv[2])
|
|
||||||
oldbal = iacct.balance()
|
|
||||||
amt = int(sys.argv[3])
|
|
||||||
cash = shop.buy_for_cash(iacct, amt);
|
|
||||||
print("Old amount {}, increased by {}, take {} from cashbox".format(oldbal, amt, cash))
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
help()
|
help()
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from PySide import QtCore, QtGui, QtDeclarative
|
from PySide import QtCore, QtGui, QtDeclarative
|
||||||
|
|
||||||
|
@ -9,15 +8,6 @@ from brmbar import Database
|
||||||
|
|
||||||
import brmbar
|
import brmbar
|
||||||
|
|
||||||
# User credit balance limit; sale will fail when balance is below this limit.
|
|
||||||
LIMIT_BALANCE = -200
|
|
||||||
# When below this credit balance, an alert hook script (see below) is run.
|
|
||||||
ALERT_BALANCE = 0
|
|
||||||
# This script is executed when a user is buying things and their balance is
|
|
||||||
# below LIMIT_BALANCE (with argument "limit") or below ALERT_BALANCE
|
|
||||||
# (with argument "alert").
|
|
||||||
ALERT_SCRIPT = "./alert.sh"
|
|
||||||
|
|
||||||
|
|
||||||
class ShopAdapter(QtCore.QObject):
|
class ShopAdapter(QtCore.QObject):
|
||||||
""" Interface between QML and the brmbar package """
|
""" Interface between QML and the brmbar package """
|
||||||
|
@ -39,14 +29,6 @@ class ShopAdapter(QtCore.QObject):
|
||||||
map["price"] = str(sell)
|
map["price"] = str(sell)
|
||||||
return map
|
return map
|
||||||
|
|
||||||
def acct_inventory_map2(self, acct):
|
|
||||||
buy, sell = 666, 666
|
|
||||||
map = acct.__dict__.copy()
|
|
||||||
map["balance"] = "{:.0f}".format(666)
|
|
||||||
map["buy_price"] = str(buy)
|
|
||||||
map["price"] = str(sell)
|
|
||||||
return map
|
|
||||||
|
|
||||||
def acct_cash_map(self, acct):
|
def acct_cash_map(self, acct):
|
||||||
map = acct.__dict__.copy()
|
map = acct.__dict__.copy()
|
||||||
return map
|
return map
|
||||||
|
@ -88,18 +70,6 @@ class ShopAdapter(QtCore.QObject):
|
||||||
db.commit()
|
db.commit()
|
||||||
return acct
|
return acct
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
|
||||||
def canSellItem(self, itemid, userid):
|
|
||||||
user = brmbar.Account.load(db, id = userid)
|
|
||||||
if -user.balance() > ALERT_BALANCE:
|
|
||||||
return True
|
|
||||||
elif -user.balance() > LIMIT_BALANCE:
|
|
||||||
subprocess.call(["sh", ALERT_SCRIPT, "alert"])
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
subprocess.call(["sh", ALERT_SCRIPT, "limit"])
|
|
||||||
return False
|
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
||||||
def sellItem(self, itemid, userid):
|
def sellItem(self, itemid, userid):
|
||||||
user = brmbar.Account.load(db, id = userid)
|
user = brmbar.Account.load(db, id = userid)
|
||||||
|
@ -115,7 +85,6 @@ class ShopAdapter(QtCore.QObject):
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
||||||
def chargeCredit(self, credit, userid):
|
def chargeCredit(self, credit, userid):
|
||||||
subprocess.call(["sh", ALERT_SCRIPT, "charge"])
|
|
||||||
user = brmbar.Account.load(db, id = userid)
|
user = brmbar.Account.load(db, id = userid)
|
||||||
shop.add_credit(credit = credit, user = user)
|
shop.add_credit(credit = credit, user = user)
|
||||||
balance = user.negbalance_str()
|
balance = user.negbalance_str()
|
||||||
|
@ -130,19 +99,6 @@ class ShopAdapter(QtCore.QObject):
|
||||||
db.commit()
|
db.commit()
|
||||||
return balance
|
return balance
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', 'QVariant', 'QVariant', result='QVariant')
|
|
||||||
def newTransfer(self, uidfrom, uidto, amount):
|
|
||||||
ufrom = brmbar.Account.load(db, id=uidfrom)
|
|
||||||
uto = brmbar.Account.load(db, id=uidto)
|
|
||||||
shop.transfer_credit(ufrom, uto, amount = amount)
|
|
||||||
db.commit()
|
|
||||||
return currency.str(float(amount))
|
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', result='QVariant')
|
|
||||||
def balance_user(self, userid):
|
|
||||||
user = brmbar.Account.load(db, id=userid)
|
|
||||||
return user.negbalance_str()
|
|
||||||
|
|
||||||
@QtCore.Slot(result='QVariant')
|
@QtCore.Slot(result='QVariant')
|
||||||
def balance_cash(self):
|
def balance_cash(self):
|
||||||
balance = shop.cash.balance_str()
|
balance = shop.cash.balance_str()
|
||||||
|
@ -172,7 +128,7 @@ class ShopAdapter(QtCore.QObject):
|
||||||
|
|
||||||
@QtCore.Slot('QVariant', result='QVariant')
|
@QtCore.Slot('QVariant', result='QVariant')
|
||||||
def itemList(self, query):
|
def itemList(self, query):
|
||||||
alist = [ self.acct_inventory_map2(a) for a in shop.account_list("inventory", like_str="%%"+query+"%%") ]
|
alist = [ self.acct_inventory_map(a) for a in shop.account_list("inventory", like_str="%%"+query+"%%") ]
|
||||||
db.commit()
|
db.commit()
|
||||||
return alist
|
return alist
|
||||||
|
|
||||||
|
|
|
@ -100,17 +100,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function chargeCredit() {
|
function chargeCredit() {
|
||||||
var balance=0
|
var balance = shop.chargeCredit(amount, userdbid)
|
||||||
if (!isNaN(amount)) {
|
status_text.setStatus("Charged! "+username+"'s credit is "+balance+".", "#ffff7c")
|
||||||
if(amount>=0) {
|
|
||||||
balance = shop.chargeCredit(amount, userdbid)
|
|
||||||
status_text.setStatus("Charged "+amount+"! "+username+"'s credit is "+balance+".", "#ffff7c")
|
|
||||||
} else {
|
|
||||||
balance = shop.withdrawCredit((amount*(-1)), userdbid)
|
|
||||||
status_text.setStatus("Withdrawn "+amount+"! "+username+"'s credit is "+balance+".", "#ffff7c")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadPage("MainPage")
|
loadPage("MainPage")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,6 @@ Item {
|
||||||
if (acct.acctype == "cash") { //Copied from BarButton.onButtonClick
|
if (acct.acctype == "cash") { //Copied from BarButton.onButtonClick
|
||||||
shop.sellItemCash(dbid)
|
shop.sellItemCash(dbid)
|
||||||
status_text.setStatus("Sold! Put " + price + " Kč in the money box.", "#ffff7c")
|
status_text.setStatus("Sold! Put " + price + " Kč in the money box.", "#ffff7c")
|
||||||
} else if (!shop.canSellItem(dbid, acct.id)) {
|
|
||||||
status_text.setStatus("NOT SOLD! "+acct.name+"'s credit is TOO LOW: "+shop.balance_user(acct.id), "#ff4444")
|
|
||||||
} else {
|
} else {
|
||||||
var balance = shop.sellItem(dbid, acct.id)
|
var balance = shop.sellItem(dbid, acct.id)
|
||||||
status_text.setStatus("Sold! "+acct.name+"'s credit is "+balance+".", "#ffff7c")
|
status_text.setStatus("Sold! "+acct.name+"'s credit is "+balance+".", "#ffff7c")
|
||||||
|
|
|
@ -33,16 +33,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BarButton {
|
|
||||||
x: 450
|
|
||||||
y: 838
|
|
||||||
width: 360
|
|
||||||
text: "Transfer"
|
|
||||||
onButtonClick: {
|
|
||||||
loadPage("Transfer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BarButton {
|
BarButton {
|
||||||
id: management
|
id: management
|
||||||
x: 855
|
x: 855
|
||||||
|
@ -53,11 +43,4 @@ Item {
|
||||||
loadPage("Management")
|
loadPage("Management")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BarButton {
|
|
||||||
x: 65
|
|
||||||
y: 438
|
|
||||||
width: 1150
|
|
||||||
text: "* Za uklid brmlabu vam nabijeme kredit. *"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
import QtQuick 1.1
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: page
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property variant userfrom: ""
|
|
||||||
property variant uidfrom: ""
|
|
||||||
property variant userto: ""
|
|
||||||
property variant uidto: ""
|
|
||||||
property string amount: amount_pad.enteredText
|
|
||||||
|
|
||||||
BarcodeInput {
|
|
||||||
color: "#00ff00" /* just for debugging */
|
|
||||||
focus: !(parent.userfrom != "" && parent.userto != "")
|
|
||||||
onAccepted: {
|
|
||||||
var acct = shop.barcodeInput(text)
|
|
||||||
text = ""
|
|
||||||
if (typeof(acct) == "undefined") {
|
|
||||||
status_text.setStatus("Unknown barcode", "#ff4444")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (acct.acctype == "debt") {
|
|
||||||
if (userfrom == "") {
|
|
||||||
userfrom = acct.name
|
|
||||||
uidfrom = acct.id
|
|
||||||
} else {
|
|
||||||
userto = acct.name
|
|
||||||
uidto = acct.id
|
|
||||||
}
|
|
||||||
} else if (acct.acctype == "recharge") {
|
|
||||||
amount = acct.amount
|
|
||||||
} else {
|
|
||||||
status_text.setStatus("Unknown barcode", "#ff4444")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: amount_row
|
|
||||||
visible: parent.userfrom != "" && parent.userto != ""
|
|
||||||
x: 65;
|
|
||||||
y: 166;
|
|
||||||
width: 890
|
|
||||||
height: 60
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: item_sellprice_label
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
height: 60
|
|
||||||
width: 200
|
|
||||||
color: "#ffffff"
|
|
||||||
text: "Money Amount:"
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.pixelSize: 0.768 * 46
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: amount_input
|
|
||||||
x: 320
|
|
||||||
y: 0
|
|
||||||
height: 60
|
|
||||||
width: 269
|
|
||||||
color: "#ffff7c"
|
|
||||||
text: amount
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.pixelSize: 0.768 * 122
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BarNumPad {
|
|
||||||
id: amount_pad
|
|
||||||
x: 65
|
|
||||||
y: 239
|
|
||||||
visible: parent.userfrom != "" && parent.userto != ""
|
|
||||||
focus: parent.userfrom != "" && parent.userto != ""
|
|
||||||
Keys.onReturnPressed: { transfer.buttonClick() }
|
|
||||||
Keys.onEscapePressed: { cancel.buttonClick() }
|
|
||||||
}
|
|
||||||
|
|
||||||
BarTextHint {
|
|
||||||
id: barcode_row
|
|
||||||
x: 65
|
|
||||||
y: parent.userfrom == "" ? 314 : 414
|
|
||||||
hint_goal: (parent.userfrom == "" ? "Take money from:" : parent.userto == "" ? "Give money to:" : parent.amount == "" ? "Specify amount" : "")
|
|
||||||
hint_action: (parent.userfrom == "" || parent.userto == "" ? "Scan barcode now" : (parent.amount ? "" : "(or scan barcode now)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: legend
|
|
||||||
visible: !(parent.userfrom != "" && parent.userto != "")
|
|
||||||
x: 65
|
|
||||||
y: 611
|
|
||||||
height: 154
|
|
||||||
width: 894
|
|
||||||
color: "#71cccc"
|
|
||||||
text: "This is for transfering credit between two brmbar users.\n May be used instead of *check next club-mate to me*."
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.pixelSize: 0.768 * 27
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: item_name
|
|
||||||
x: 422
|
|
||||||
y: 156
|
|
||||||
width: 537
|
|
||||||
height: 80
|
|
||||||
color: "#ffffff"
|
|
||||||
text: parent.userfrom ? parent.userfrom + " →" : "Money Transfer"
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.pixelSize: 0.768 * 60
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: item_name2
|
|
||||||
x: 422
|
|
||||||
y: 256
|
|
||||||
width: 537
|
|
||||||
height: 80
|
|
||||||
color: "#ffffff"
|
|
||||||
text: parent.userto ? "→ " + parent.userto : ""
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font.pixelSize: 0.768 * 60
|
|
||||||
}
|
|
||||||
|
|
||||||
BarButton {
|
|
||||||
id: transfer
|
|
||||||
x: 65
|
|
||||||
y: 838
|
|
||||||
width: 360
|
|
||||||
text: "Transfer"
|
|
||||||
onButtonClick: {
|
|
||||||
if (userfrom == "") {
|
|
||||||
status_text.setStatus("Select FROM account.", "#ff4444")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (userto == "") {
|
|
||||||
status_text.setStatus("Select TO account.", "#ff4444")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (amount == "") {
|
|
||||||
status_text.setStatus("Enter amount.", "#ff4444")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var amount_str = shop.newTransfer(uidfrom, uidto, amount)
|
|
||||||
if (typeof(amount_str) == "undefined") {
|
|
||||||
status_text.setStatus("Transfer error.", "#ff4444")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
status_text.setStatus("Transferred " + amount_str + " from " + userfrom + " to " + userto, "#ffff7c")
|
|
||||||
loadPage("MainPage")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BarButton {
|
|
||||||
id: cancel
|
|
||||||
x: 855
|
|
||||||
y: 838
|
|
||||||
width: 360
|
|
||||||
text: "Cancel"
|
|
||||||
onButtonClick: {
|
|
||||||
status_text.setStatus("Transfer cancelled", "#ff4444")
|
|
||||||
loadPage("MainPage")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -100,17 +100,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function withdrawCredit() {
|
function withdrawCredit() {
|
||||||
var balance=0
|
var balance = shop.withdrawCredit(amount, userdbid)
|
||||||
if (!isNaN(amount)) {
|
status_text.setStatus("Withdrawn! "+username+"'s credit is "+balance+".", "#ffff7c")
|
||||||
amount=(amount*1)
|
|
||||||
if(amount>=0) {
|
|
||||||
balance = shop.withdrawCredit(amount, userdbid)
|
|
||||||
status_text.setStatus("Withdrawn "+amount+"! "+username+"'s credit is "+balance+".", "#ffff7c")
|
|
||||||
} else {
|
|
||||||
balance = shop.chargeCredit((amount*(-1)),userdbid)
|
|
||||||
status_text.setStatus("Charged "+amount+"! "+username+"'s credit is "+balance+".", "#ffff7c")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loadPage("MainPage")
|
loadPage("MainPage")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
|
@ -40,22 +40,14 @@ class Account:
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, db, name, currency, acctype):
|
def create(cls, db, name, currency, acctype):
|
||||||
""" Constructor for new account """
|
""" Constructor for new account """
|
||||||
# id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype])
|
id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype])
|
||||||
id = db.execute_and_fetch("SELECT public.create_account(%s, %s, %s)", [name, currency.id, acctype])
|
id = id[0]
|
||||||
# id = id[0]
|
|
||||||
return cls(db, name = name, id = id, currency = currency, acctype = acctype)
|
return cls(db, name = name, id = id, currency = currency, acctype = acctype)
|
||||||
|
|
||||||
def balance(self):
|
def balance(self):
|
||||||
bal = self.db.execute_and_fetch(
|
crbalance = self.db.execute_and_fetch("SELECT crbalance FROM accounts WHERE id = %s", [self.id])
|
||||||
"SELECT public.compute_account_balance(%s)",
|
crbalance = crbalance[0] or 0
|
||||||
[self.id]
|
return crbalance
|
||||||
)[0]
|
|
||||||
return bal
|
|
||||||
#debit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'debit'])
|
|
||||||
#debit = debit[0] or 0
|
|
||||||
#credit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'credit'])
|
|
||||||
#credit = credit[0] or 0
|
|
||||||
#return debit - credit
|
|
||||||
|
|
||||||
def balance_str(self):
|
def balance_str(self):
|
||||||
return self.currency.str(self.balance())
|
return self.currency.str(self.balance())
|
||||||
|
@ -69,16 +61,17 @@ class Account:
|
||||||
def credit(self, transaction, amount, memo):
|
def credit(self, transaction, amount, memo):
|
||||||
return self._transaction_split(transaction, 'credit', amount, memo)
|
return self._transaction_split(transaction, 'credit', amount, memo)
|
||||||
|
|
||||||
|
# XXX atomicita
|
||||||
def _transaction_split(self, transaction, side, amount, memo):
|
def _transaction_split(self, transaction, side, amount, memo):
|
||||||
""" Common part of credit() and debit(). """
|
""" Common part of credit() and debit(). """
|
||||||
self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, self.id, amount, memo])
|
self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, self.id, amount, memo])
|
||||||
|
|
||||||
|
self.db.execute("UPDATE accounts set crbalance = crbalance + (CASE WHEN %s = 'credit' THEN -%s ELSE %s END)", [side,amount,amount])
|
||||||
|
|
||||||
def add_barcode(self, barcode):
|
def add_barcode(self, barcode):
|
||||||
# self.db.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode])
|
self.db.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode])
|
||||||
self.db.execute("SELECT public.add_barcode_to_account(%s, %s)", [self.id, barcode])
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
def rename(self, name):
|
def rename(self, name):
|
||||||
# self.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id])
|
self.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id])
|
||||||
self.db.execute("SELECT public.rename_account(%s, %s)", [self.id, name])
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
|
@ -31,34 +31,15 @@ class Currency:
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, db, name):
|
def create(cls, db, name):
|
||||||
""" Constructor for new currency """
|
""" Constructor for new currency """
|
||||||
# id = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name])
|
id = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name])
|
||||||
id = db.execute_and_fetch("SELECT public.create_currency(%s)", [name])
|
id = id[0]
|
||||||
# id = id[0]
|
|
||||||
return cls(db, name = name, id = id)
|
return cls(db, name = name, id = id)
|
||||||
|
|
||||||
def rates(self, other):
|
def rates(self, other):
|
||||||
""" Return tuple ($buy, $sell) of rates of $self in relation to $other (brmbar.Currency):
|
""" Return tuple ($buy, $sell) of rates of $self in relation to $other (brmbar.Currency):
|
||||||
$buy is the price of $self in means of $other when buying it (into brmbar)
|
$buy is the price of $self in means of $other when buying it (into brmbar)
|
||||||
$sell is the price of $self in means of $other when selling it (from brmbar) """
|
$sell is the price of $self in means of $other when selling it (from brmbar) """
|
||||||
# buy rate
|
|
||||||
res = self.db.execute_and_fetch("SELECT public.find_buy_rate(%s, %s)",[self.id, other.id])
|
|
||||||
if res is None:
|
|
||||||
raise NameError("Something fishy in find_buy_rate.");
|
|
||||||
buy = res[0]
|
|
||||||
if buy < 0:
|
|
||||||
raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name())
|
|
||||||
# sell rate
|
|
||||||
res = self.db.execute_and_fetch("SELECT public.find_sell_rate(%s, %s)",[self.id, other.id])
|
|
||||||
if res is None:
|
|
||||||
raise NameError("Something fishy in find_sell_rate.");
|
|
||||||
sell = res[0]
|
|
||||||
if sell < 0:
|
|
||||||
raise NameError("Currency.rate(): Unknown conversion " + self.name() + " to " + other.name())
|
|
||||||
|
|
||||||
return (buy, sell)
|
|
||||||
|
|
||||||
def rates2(self, other):
|
|
||||||
# the original code for compare testing
|
|
||||||
res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [self.id, other.id])
|
res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [self.id, other.id])
|
||||||
if res is None:
|
if res is None:
|
||||||
raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name())
|
raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name())
|
||||||
|
@ -73,7 +54,6 @@ class Currency:
|
||||||
|
|
||||||
return (buy, sell)
|
return (buy, sell)
|
||||||
|
|
||||||
|
|
||||||
def convert(self, amount, target):
|
def convert(self, amount, target):
|
||||||
res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [target.id, self.id])
|
res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [target.id, self.id])
|
||||||
if res is None:
|
if res is None:
|
||||||
|
@ -89,10 +69,6 @@ class Currency:
|
||||||
return "{:.2f} {}".format(amount, self.name)
|
return "{:.2f} {}".format(amount, self.name)
|
||||||
|
|
||||||
def update_sell_rate(self, target, rate):
|
def update_sell_rate(self, target, rate):
|
||||||
# self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [self.id, target.id, rate, "source_to_target"])
|
self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [self.id, target.id, rate, "source_to_target"])
|
||||||
self.db.execute("SELECT public.update_currency_sell_rate(%s, %s, %s)",
|
|
||||||
[self.id, target.id, rate])
|
|
||||||
def update_buy_rate(self, source, rate):
|
def update_buy_rate(self, source, rate):
|
||||||
# self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [source.id, self.id, rate, "target_to_source"])
|
self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [source.id, self.id, rate, "target_to_source"])
|
||||||
self.db.execute("SELECT public.update_currency_buy_rate(%s, %s, %s)",
|
|
||||||
[source.id, self.id, rate])
|
|
||||||
|
|
|
@ -25,123 +25,75 @@ class Shop:
|
||||||
deficit = Account.load(db, name = "BrmBar Deficit"))
|
deficit = Account.load(db, name = "BrmBar Deficit"))
|
||||||
|
|
||||||
def sell(self, item, user, amount = 1):
|
def sell(self, item, user, amount = 1):
|
||||||
# Call the stored procedure for the sale
|
|
||||||
cost = self.db.execute_and_fetch(
|
|
||||||
"SELECT public.sell_item(%s, %s, %s, %s, %s)",
|
|
||||||
[item.id, amount, user.id, self.currency.id, f"BrmBar sale of {amount}x {item.name} to {user.name}"]
|
|
||||||
)[0]#[0]
|
|
||||||
|
|
||||||
self.db.commit()
|
|
||||||
return cost
|
|
||||||
# Sale: Currency conversion from item currency to shop currency
|
# Sale: Currency conversion from item currency to shop currency
|
||||||
#(buy, sell) = item.currency.rates(self.currency)
|
(buy, sell) = item.currency.rates(self.currency)
|
||||||
#cost = amount * sell
|
cost = amount * sell
|
||||||
#profit = amount * (sell - buy)
|
profit = amount * (sell - buy)
|
||||||
|
|
||||||
#transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name))
|
transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name))
|
||||||
#item.credit(transaction, amount, user.name)
|
item.credit(transaction, amount, user.name)
|
||||||
#user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account
|
user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account
|
||||||
#self.profits.debit(transaction, profit, "Margin on " + item.name)
|
self.profits.debit(transaction, profit, "Margin on " + item.name)
|
||||||
#self.db.commit()
|
self.db.commit()
|
||||||
#return cost
|
|
||||||
|
return cost
|
||||||
|
|
||||||
def sell_for_cash(self, item, amount = 1):
|
def sell_for_cash(self, item, amount = 1):
|
||||||
cost = self.db.execute_and_fetch(
|
# Sale: Currency conversion from item currency to shop currency
|
||||||
"SELECT public.sell_item_for_cash(%s, %s, %s, %s, %s)",
|
(buy, sell) = item.currency.rates(self.currency)
|
||||||
[item.id, amount, user.id, self.currency.id, f"BrmBar sale of {amount}x {item.name} for cash"]
|
cost = amount * sell
|
||||||
)[0]#[0]
|
profit = amount * (sell - buy)
|
||||||
|
|
||||||
|
transaction = self._transaction(description = "BrmBar sale of {}x {} for cash".format(amount, item.name))
|
||||||
|
item.credit(transaction, amount, "Cash")
|
||||||
|
self.cash.debit(transaction, cost, item.name)
|
||||||
|
self.profits.debit(transaction, profit, "Margin on " + item.name)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
return cost
|
return cost
|
||||||
## Sale: Currency conversion from item currency to shop currency
|
|
||||||
#(buy, sell) = item.currency.rates(self.currency)
|
|
||||||
#cost = amount * sell
|
|
||||||
#profit = amount * (sell - buy)
|
|
||||||
|
|
||||||
#transaction = self._transaction(description = "BrmBar sale of {}x {} for cash".format(amount, item.name))
|
|
||||||
#item.credit(transaction, amount, "Cash")
|
|
||||||
#self.cash.debit(transaction, cost, item.name)
|
|
||||||
#self.profits.debit(transaction, profit, "Margin on " + item.name)
|
|
||||||
#self.db.commit()
|
|
||||||
|
|
||||||
#return cost
|
|
||||||
|
|
||||||
def undo_sale(self, item, user, amount = 1):
|
def undo_sale(self, item, user, amount = 1):
|
||||||
# Undo sale; rarely needed
|
# Undo sale; rarely needed
|
||||||
#(buy, sell) = item.currency.rates(self.currency)
|
(buy, sell) = item.currency.rates(self.currency)
|
||||||
#cost = amount * sell
|
cost = amount * sell
|
||||||
#profit = amount * (sell - buy)
|
profit = amount * (sell - buy)
|
||||||
|
|
||||||
#transaction = self._transaction(responsible = user, description = "BrmBar sale UNDO of {}x {} to {}".format(amount, item.name, user.name))
|
|
||||||
#item.debit(transaction, amount, user.name + " (sale undo)")
|
|
||||||
#user.credit(transaction, cost, item.name + " (sale undo)")
|
|
||||||
#self.profits.credit(transaction, profit, "Margin repaid on " + item.name)
|
|
||||||
# Call the stored procedure for undoing a sale
|
|
||||||
cost = self.db.execute_and_fetch(
|
|
||||||
"SELECT public.undo_sale_of_item(%s, %s, %s, %s)",
|
|
||||||
[item.id, amount, user.id, user.currency.id, f"BrmBar sale UNDO of {amount}x {item.name} to {user.name}"]
|
|
||||||
)[0]#[0]
|
|
||||||
|
|
||||||
|
transaction = self._transaction(responsible = user, description = "BrmBar sale UNDO of {}x {} to {}".format(amount, item.name, user.name))
|
||||||
|
item.debit(transaction, amount, user.name + " (sale undo)")
|
||||||
|
user.credit(transaction, cost, item.name + " (sale undo)")
|
||||||
|
self.profits.credit(transaction, profit, "Margin repaid on " + item.name)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
return cost
|
return cost
|
||||||
|
|
||||||
def add_credit(self, credit, user):
|
def add_credit(self, credit, user):
|
||||||
self.db.execute_and_fetch(
|
transaction = self._transaction(responsible = user, description = "BrmBar credit replenishment for " + user.name)
|
||||||
"SELECT public.add_credit(%s, %s, %s, %s)",
|
self.cash.debit(transaction, credit, user.name)
|
||||||
[self.cash.id, credit, user.id, user.name]
|
user.credit(transaction, credit, "Credit replenishment")
|
||||||
)
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
#transaction = self._transaction(responsible = user, description = "BrmBar credit replenishment for " + user.name)
|
|
||||||
#self.cash.debit(transaction, credit, user.name)
|
|
||||||
#user.credit(transaction, credit, "Credit replenishment")
|
|
||||||
#self.db.commit()
|
|
||||||
|
|
||||||
def withdraw_credit(self, credit, user):
|
def withdraw_credit(self, credit, user):
|
||||||
self.db.execute_and_fetch(
|
transaction = self._transaction(responsible = user, description = "BrmBar credit withdrawal for " + user.name)
|
||||||
"SELECT public.withdraw_credit(%s, %s, %s, %s)",
|
self.cash.credit(transaction, credit, user.name)
|
||||||
[self.cash.id, credit, user.id, user.name]
|
user.debit(transaction, credit, "Credit withdrawal")
|
||||||
)
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
#transaction = self._transaction(responsible = user, description = "BrmBar credit withdrawal for " + user.name)
|
|
||||||
#self.cash.credit(transaction, credit, user.name)
|
|
||||||
#user.debit(transaction, credit, "Credit withdrawal")
|
|
||||||
#self.db.commit()
|
|
||||||
|
|
||||||
def transfer_credit(self, userfrom, userto, amount):
|
|
||||||
self.db.execute_and_fetch(
|
|
||||||
"SELECT public.transfer_credit(%s, %s, %s, %s)",
|
|
||||||
[self.cash.id, credit, user.id, user.name]
|
|
||||||
)
|
|
||||||
self.db.commit()
|
|
||||||
#self.add_credit(amount, userto)
|
|
||||||
#self.withdraw_credit(amount, userfrom)
|
|
||||||
|
|
||||||
def buy_for_cash(self, item, amount = 1):
|
def buy_for_cash(self, item, amount = 1):
|
||||||
cost = self.db.execute_and_fetch(
|
|
||||||
"SELECT public.buy_for_cash(%s, %s, %s, %s, %s)",
|
|
||||||
[self.cash.id, item.id, amount, self.currency.id, item.name]
|
|
||||||
)[0]
|
|
||||||
# Buy: Currency conversion from item currency to shop currency
|
# Buy: Currency conversion from item currency to shop currency
|
||||||
#(buy, sell) = item.currency.rates(self.currency)
|
(buy, sell) = item.currency.rates(self.currency)
|
||||||
#cost = amount * buy
|
cost = amount * buy
|
||||||
|
|
||||||
#transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name))
|
transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name))
|
||||||
#item.debit(transaction, amount, "Cash")
|
item.debit(transaction, amount, "Cash")
|
||||||
#self.cash.credit(transaction, cost, item.name)
|
self.cash.credit(transaction, cost, item.name)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
return cost
|
return cost
|
||||||
|
|
||||||
def receipt_to_credit(self, user, credit, description):
|
def receipt_to_credit(self, user, credit, description):
|
||||||
#transaction = self._transaction(responsible = user, description = "Receipt: " + description)
|
transaction = self._transaction(responsible = user, description = "Receipt: " + description)
|
||||||
#self.profits.credit(transaction, credit, user.name)
|
self.profits.credit(transaction, credit, user.name)
|
||||||
#user.credit(transaction, credit, "Credit from receipt: " + description)
|
user.credit(transaction, credit, "Credit from receipt: " + description)
|
||||||
self.db.execute_and_fetch(
|
|
||||||
"SELECT public.buy_for_cash(%s, %s, %s, %s, %s)",
|
|
||||||
[self.profits.id, user.id, user.name, credit, description]
|
|
||||||
)[0]
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
def _transaction(self, responsible = None, description = None):
|
def _transaction(self, responsible = None, description = None):
|
||||||
|
@ -150,25 +102,18 @@ class Shop:
|
||||||
transaction = transaction[0]
|
transaction = transaction[0]
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def credit_balance(self, overflow=None):
|
def credit_balance(self):
|
||||||
# We assume all debt accounts share a currency
|
# We assume all debt accounts share a currency
|
||||||
sumselect = """
|
sumselect = """
|
||||||
SELECT SUM(ts.amount)
|
SELECT SUM(crbalance)
|
||||||
FROM accounts AS a
|
FROM accounts WHERE acctype = %s
|
||||||
LEFT JOIN transaction_splits AS ts ON a.id = ts.account
|
|
||||||
WHERE a.acctype = %s AND ts.side = %s
|
|
||||||
"""
|
"""
|
||||||
if overflow is not None:
|
cur = self.db.execute_and_fetch(sumselect, ["debt"])
|
||||||
sumselect += ' AND a.name ' + ('NOT ' if overflow == 'exclude' else '') + ' LIKE \'%%-overflow\''
|
cur = cur[0] or 0
|
||||||
cur = self.db.execute_and_fetch(sumselect, ["debt", 'debit'])
|
return cur
|
||||||
debit = cur[0] or 0
|
def credit_negbalance_str(self):
|
||||||
credit = self.db.execute_and_fetch(sumselect, ["debt", 'credit'])
|
return self.currency.str(-self.credit_balance())
|
||||||
credit = credit[0] or 0
|
|
||||||
return debit - credit
|
|
||||||
def credit_negbalance_str(self, overflow=None):
|
|
||||||
return self.currency.str(-self.credit_balance(overflow=overflow))
|
|
||||||
|
|
||||||
# XXX causing extra heavy delay ( thousands of extra SQL queries ), disabled
|
|
||||||
def inventory_balance(self):
|
def inventory_balance(self):
|
||||||
balance = 0
|
balance = 0
|
||||||
# Each inventory account has its own currency,
|
# Each inventory account has its own currency,
|
||||||
|
@ -181,116 +126,75 @@ class Shop:
|
||||||
# might have been bought for a different price! Therefore,
|
# might have been bought for a different price! Therefore,
|
||||||
# we need to replace the command below with a complex SQL
|
# we need to replace the command below with a complex SQL
|
||||||
# statement that will... ugh, accounting is hard!
|
# statement that will... ugh, accounting is hard!
|
||||||
b = inv.balance() * inv.currency.rates(self.currency)[0]
|
balance += inv.balance() * inv.currency.rates(self.currency)[0]
|
||||||
# if b != 0:
|
|
||||||
# print(str(b) + ',' + inv.name)
|
|
||||||
balance += b
|
|
||||||
return balance
|
return balance
|
||||||
|
|
||||||
# XXX bypass hack
|
|
||||||
def inventory_balance_str(self):
|
def inventory_balance_str(self):
|
||||||
# return self.currency.str(self.inventory_balance())
|
return self.currency.str(self.inventory_balance())
|
||||||
return "XXX"
|
|
||||||
|
|
||||||
def account_list(self, acctype, like_str="%%"):
|
def account_list(self, acctype, like_str="%%"):
|
||||||
"""list all accounts (people or items, as per acctype)"""
|
"""list all accounts (people or items, as per acctype)"""
|
||||||
accts = []
|
accts = []
|
||||||
cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s AND name ILIKE %s ORDER BY name ASC", [acctype, like_str])
|
cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s AND name ILIKE %s ORDER BY name ASC", [acctype, like_str])
|
||||||
#FIXME: sanitize input like_str ^
|
#FIXME: sanitize input like_str ^
|
||||||
for inventory in cur:
|
for inventory in cur:
|
||||||
accts += [ Account.load(self.db, id = inventory[0]) ]
|
accts += [ Account.load(self.db, id = inventory[0]) ]
|
||||||
return accts
|
return accts
|
||||||
|
|
||||||
def fix_inventory(self, item, amount):
|
def fix_inventory(self, item, amount):
|
||||||
rv = self.db.execute_and_fetch(
|
amount_in_reality = amount
|
||||||
"SELECT public.fix_inventory(%s, %s, %s, %s, %s, %s)",
|
amount_in_system = item.balance()
|
||||||
[item.id, item.currency.id, self.excess.id, self.deficit.id, self.currency.id, amount]
|
(buy, sell) = item.currency.rates(self.currency)
|
||||||
)[0]
|
|
||||||
|
|
||||||
self.db.commit()
|
|
||||||
return rv
|
|
||||||
#amount_in_reality = amount
|
|
||||||
#amount_in_system = item.balance()
|
|
||||||
#(buy, sell) = item.currency.rates(self.currency)
|
|
||||||
|
|
||||||
#diff = abs(amount_in_reality - amount_in_system)
|
|
||||||
#buy_total = buy * diff
|
|
||||||
#if amount_in_reality > amount_in_system:
|
|
||||||
# transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
|
||||||
# item.debit(transaction, diff, "Inventory fix excess")
|
|
||||||
# self.excess.credit(transaction, buy_total, "Inventory fix excess " + item.name)
|
|
||||||
# self.db.commit()
|
|
||||||
# return True
|
|
||||||
#elif amount_in_reality < amount_in_system:
|
|
||||||
# transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
|
||||||
# item.credit(transaction, diff, "Inventory fix deficit")
|
|
||||||
# self.deficit.debit(transaction, buy_total, "Inventory fix deficit " + item.name)
|
|
||||||
# self.db.commit()
|
|
||||||
# return True
|
|
||||||
#else:
|
|
||||||
# transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
|
||||||
# item.debit(transaction, 0, "Inventory fix - amount was correct")
|
|
||||||
# item.credit(transaction, 0, "Inventory fix - amount was correct")
|
|
||||||
# self.db.commit()
|
|
||||||
# return False
|
|
||||||
|
|
||||||
|
diff = abs(amount_in_reality - amount_in_system)
|
||||||
|
buy_total = buy * diff
|
||||||
|
if amount_in_reality > amount_in_system:
|
||||||
|
transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
||||||
|
item.debit(transaction, diff, "Inventory fix excess")
|
||||||
|
self.excess.credit(transaction, buy_total, "Inventory fix excess " + item.name)
|
||||||
|
self.db.commit()
|
||||||
|
return True
|
||||||
|
elif amount_in_reality < amount_in_system:
|
||||||
|
transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
||||||
|
item.credit(transaction, diff, "Inventory fix deficit")
|
||||||
|
self.deficit.debit(transaction, buy_total, "Inventory fix deficit " + item.name)
|
||||||
|
self.db.commit()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality))
|
||||||
|
item.debit(transaction, 0, "Inventory fix - amount was correct")
|
||||||
|
item.credit(transaction, 0, "Inventory fix - amount was correct")
|
||||||
|
self.db.commit()
|
||||||
|
return False
|
||||||
def fix_cash(self, amount):
|
def fix_cash(self, amount):
|
||||||
rv = self.db.execute_and_fetch(
|
amount_in_reality = amount
|
||||||
"SELECT public.fix_cash(%s, %s, %s, %s)",
|
amount_in_system = self.cash.balance()
|
||||||
[self.excess.id, self.deficit.id, self.currency.id, amount]
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
self.db.commit()
|
|
||||||
return rv
|
|
||||||
#amount_in_reality = amount
|
|
||||||
#amount_in_system = self.cash.balance()
|
|
||||||
|
|
||||||
#diff = abs(amount_in_reality - amount_in_system)
|
|
||||||
#if amount_in_reality > amount_in_system:
|
|
||||||
# transaction = self._transaction(description = "BrmBar cash inventory fix of {} in system to {} in reality".format(amount_in_system, amount_in_reality))
|
|
||||||
# self.cash.debit(transaction, diff, "Inventory fix excess")
|
|
||||||
# self.excess.credit(transaction, diff, "Inventory cash fix excess.")
|
|
||||||
# self.db.commit()
|
|
||||||
# return True
|
|
||||||
#elif amount_in_reality < amount_in_system:
|
|
||||||
# transaction = self._transaction(description = "BrmBar cash inventory fix of {} in system to {} in reality".format(amount_in_system, amount_in_reality))
|
|
||||||
# self.cash.credit(transaction, diff, "Inventory fix deficit")
|
|
||||||
# self.deficit.debit(transaction, diff, "Inventory fix deficit.")
|
|
||||||
# self.db.commit()
|
|
||||||
# return True
|
|
||||||
#else:
|
|
||||||
# return False
|
|
||||||
|
|
||||||
|
diff = abs(amount_in_reality - amount_in_system)
|
||||||
|
if amount_in_reality > amount_in_system:
|
||||||
|
transaction = self._transaction(description = "BrmBar cash inventory fix of {} in system to {} in reality".format(amount_in_system, amount_in_reality))
|
||||||
|
self.cash.debit(transaction, diff, "Inventory fix excess")
|
||||||
|
self.excess.credit(transaction, diff, "Inventory cash fix excess.")
|
||||||
|
self.db.commit()
|
||||||
|
return True
|
||||||
|
elif amount_in_reality < amount_in_system:
|
||||||
|
transaction = self._transaction(description = "BrmBar cash inventory fix of {} in system to {} in reality".format(amount_in_system, amount_in_reality))
|
||||||
|
self.cash.credit(transaction, diff, "Inventory fix deficit")
|
||||||
|
self.deficit.debit(transaction, diff, "Inventory fix deficit.")
|
||||||
|
self.db.commit()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
def consolidate(self):
|
def consolidate(self):
|
||||||
msg = self.db.execute_and_fetch(
|
transaction = self._transaction(description = "BrmBar inventory consolidation")
|
||||||
"SELECT public.make_consolidate_transaction(%s, %s, %s)",
|
|
||||||
[self.excess.id, self.deficit.id, self.profits.id]
|
|
||||||
)[0]
|
|
||||||
#transaction = self._transaction(description = "BrmBar inventory consolidation")
|
|
||||||
#excess_balance = self.excess.balance()
|
|
||||||
#if excess_balance != 0:
|
|
||||||
# print("Excess balance {} debited to profit".format(-excess_balance))
|
|
||||||
# self.excess.debit(transaction, -excess_balance, "Excess balance added to profit.")
|
|
||||||
# self.profits.debit(transaction, -excess_balance, "Excess balance added to profit.")
|
|
||||||
#deficit_balance = self.deficit.balance()
|
|
||||||
#if deficit_balance != 0:
|
|
||||||
# print("Deficit balance {} credited to profit".format(deficit_balance))
|
|
||||||
# self.deficit.credit(transaction, deficit_balance, "Deficit balance removed from profit.")
|
|
||||||
# self.profits.credit(transaction, deficit_balance, "Deficit balance removed from profit.")
|
|
||||||
if msg != None:
|
|
||||||
print(msg)
|
|
||||||
self.db.commit()
|
|
||||||
|
|
||||||
def undo(self, oldtid):
|
excess_balance = self.excess.balance()
|
||||||
#description = self.db.execute_and_fetch("SELECT description FROM transactions WHERE id = %s", [oldtid])[0]
|
if excess_balance != 0:
|
||||||
#description = 'undo %d (%s)' % (oldtid, description)
|
print("Excess balance {} debited to profit".format(-excess_balance))
|
||||||
|
self.excess.debit(transaction, -excess_balance, "Excess balance added to profit.")
|
||||||
#transaction = self._transaction(description=description)
|
self.profits.debit(transaction, -excess_balance, "Excess balance added to profit.")
|
||||||
#for split in self.db.execute_and_fetchall("SELECT id, side, account, amount, memo FROM transaction_splits WHERE transaction = %s", [oldtid]):
|
deficit_balance = self.deficit.balance()
|
||||||
# splitid, side, account, amount, memo = split
|
if deficit_balance != 0:
|
||||||
# memo = 'undo %d (%s)' % (splitid, memo)
|
print("Deficit balance {} credited to profit".format(deficit_balance))
|
||||||
# amount = -amount
|
self.deficit.credit(transaction, deficit_balance, "Deficit balance removed from profit.")
|
||||||
# self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, account, amount, memo])
|
self.profits.credit(transaction, deficit_balance, "Deficit balance removed from profit.")
|
||||||
transaction = self.db.execute_and_fetch("SELECT public.undo_transaction(%s)",[oldtid])[0]
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
return transaction
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# cleanup bounty
|
|
||||||
*/5 * * * * ~/brmbar/brmbar3/uklid-watchdog.sh
|
|
||||||
0 0 * * 1 ~/brmbar/brmbar3/uklid-refill.sh
|
|
||||||
# overall summary
|
|
||||||
5 4 * * * ~/brmbar/brmbar3/daily-summary.sh | mail -s "daily brmbar summary" yyy@yyy
|
|
||||||
# debt track
|
|
||||||
5 0 * * * ~/brmbar/brmbar3/dluhy.sh 2>/dev/null
|
|
||||||
|
|
||||||
# per-user summary
|
|
||||||
1 0 * * * /home/brmlab/brmbar/brmbar3/log.sh yyy yyy@yyy
|
|
||||||
|
|
||||||
# backup
|
|
||||||
6 * * * * echo "SELECT * FROM account_balances;" | psql brmbar | gzip -9 | ssh -Tp 110 -i /home/brmlab/.ssh/id_ecdsa jenda@coralmyn.hrach.eu
|
|
||||||
16 1 * * * pg_dump brmbar | gzip -9 | ssh -Tp 110 -i /home/brmlab/.ssh/id_ecdsa jenda@coralmyn.hrach.eu
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
p1=`echo -n "brmbar - dluhy: "; echo "SELECT name, crbalance FROM account_balances WHERE acctype = 'debt' AND crbalance < -100 AND name NOT LIKE '%overflow%' AND name NOT LIKE 'sachyo' ORDER BY crbalance ASC" | psql brmbar | tail -n +3 | grep '|' | tr -s " " | sed -e "s/ |/:/g" -e "s/$/;/" | tr -d "\n"`
|
|
||||||
p2=`echo "SELECT sum(crbalance) FROM account_balances WHERE acctype = 'debt' AND crbalance < 0 AND name NOT LIKE '%overflow%' AND name NOT LIKE 'sachyo'" | psql brmbar | tail -n +3 | head -n 1 | tr -s " "`
|
|
||||||
echo "$p1 total$p2 Kc. https://www.elektro-obojky.cz/" | ssh -p 110 -i /home/brmlab/.ssh/id_rsa jenda@coralmyn.hrach.eu
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
p=`/home/brmlab/brmbar/brmbar3/brmbar-cli.py userlog "$1" yesterday`
|
|
||||||
|
|
||||||
if [ -n "$p" ]; then
|
|
||||||
echo "$p" | mail -s "brmbar report" "$2"
|
|
||||||
fi
|
|
|
@ -1,313 +0,0 @@
|
||||||
--
|
|
||||||
-- 0001-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 BOOLEAN
|
|
||||||
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 VOID
|
|
||||||
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,40 +0,0 @@
|
||||||
--
|
|
||||||
-- 0002-trading-accounts.sql
|
|
||||||
--
|
|
||||||
-- #2 - add trading accounts to account type type
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(1) THEN
|
|
||||||
|
|
||||||
ALTER TYPE public.account_type ADD VALUE 'trading';
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(2);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,52 +0,0 @@
|
||||||
--
|
|
||||||
-- 0003-new-account.sql
|
|
||||||
--
|
|
||||||
-- #3 - stored procedure for creating new account
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(2) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.create_account(
|
|
||||||
IN i_name public.accounts.name%TYPE,
|
|
||||||
IN i_currency public.accounts.currency%TYPE,
|
|
||||||
IN i_acctype public.accounts.acctype%TYPE
|
|
||||||
) RETURNS INTEGER LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
r_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO public.accounts (name, currency, acctype)
|
|
||||||
VALUES (i_name, i_currency, i_acctype) RETURNING id INTO r_id;
|
|
||||||
RETURN r_id;
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(3);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,50 +0,0 @@
|
||||||
--
|
|
||||||
-- 0004-add-account-barcode.sql
|
|
||||||
--
|
|
||||||
-- #4 - stored procedure for adding barcode to account
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(3) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.add_barcode_to_account(
|
|
||||||
IN i_account public.barcodes.account%TYPE,
|
|
||||||
IN i_barcode public.barcodes.barcode%TYPE
|
|
||||||
) RETURNS VOID LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
r_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO public.barcodes (account, barcode)
|
|
||||||
VALUES (i_account, i_barcode);
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(4);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,51 +0,0 @@
|
||||||
--
|
|
||||||
-- 0005-rename-account.sql
|
|
||||||
--
|
|
||||||
-- #5 - stored procedure for renaming account
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(4) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.rename_account(
|
|
||||||
IN i_account public.accounts.id%TYPE,
|
|
||||||
IN i_name public.accounts.name%TYPE
|
|
||||||
) RETURNS VOID LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
r_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
UPDATE public.accounts
|
|
||||||
SET name = i_name
|
|
||||||
WHERE id = i_account;
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(5);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,50 +0,0 @@
|
||||||
--
|
|
||||||
-- 0006-new-currency.sql
|
|
||||||
--
|
|
||||||
-- #6 - stored procedure for creating new currency
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(5) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.create_currency(
|
|
||||||
IN i_name public.currencies.name%TYPE
|
|
||||||
) RETURNS INTEGER LANGUAGE plpgsql AS $$
|
|
||||||
DECLARE
|
|
||||||
r_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO public.currencies (name)
|
|
||||||
VALUES (i_name) RETURNING id INTO r_id;
|
|
||||||
RETURN r_id;
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(6);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,49 +0,0 @@
|
||||||
--
|
|
||||||
-- 0007-update-currency-sell-rate.sql
|
|
||||||
--
|
|
||||||
-- #7 - stored procedure for updating sell rate
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(6) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.update_currency_sell_rate(
|
|
||||||
IN i_currency public.exchange_rates.source%TYPE,
|
|
||||||
IN i_target public.exchange_rates.target%TYPE,
|
|
||||||
IN i_rate public.exchange_rates.rate%TYPE
|
|
||||||
) RETURNS VOID LANGUAGE plpgsql AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO public.exchange_rates(source, target, rate, rate_dir)
|
|
||||||
VALUES (i_currency, i_target, i_rate, 'source_to_target');
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(7);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,49 +0,0 @@
|
||||||
--
|
|
||||||
-- 0008-update-currency-buy-rate.sql
|
|
||||||
--
|
|
||||||
-- #8 - stored procedure for updating buy rate
|
|
||||||
--
|
|
||||||
-- 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.
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Require fully-qualified names
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(7) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.update_currency_buy_rate(
|
|
||||||
IN i_currency public.exchange_rates.target%TYPE,
|
|
||||||
IN i_source public.exchange_rates.source%TYPE,
|
|
||||||
IN i_rate public.exchange_rates.rate%TYPE
|
|
||||||
) RETURNS VOID LANGUAGE plpgsql AS $$
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO public.exchange_rates(source, target, rate, rate_dir)
|
|
||||||
VALUES (i_source, i_currency, i_rate, 'target_to_source');
|
|
||||||
END
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(8);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
|
@ -1,149 +0,0 @@
|
||||||
--
|
|
||||||
-- 0009-shop-sell.sql
|
|
||||||
--
|
|
||||||
-- #9 - stored function for sell transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(8) THEN
|
|
||||||
|
|
||||||
-- return negative number on rate not found
|
|
||||||
CREATE OR REPLACE FUNCTION public.find_buy_rate(
|
|
||||||
IN i_item_id public.accounts.id%TYPE;
|
|
||||||
IN i_other_id public.accounts.id%TYPE;
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_rate public.exchange_rates.rate%TYPE;
|
|
||||||
v_rate_dir public.exchange_rates.rate_dir%TYPE;
|
|
||||||
BEGIN
|
|
||||||
SELECT rate INTO STRICT v_rate, rate_dir INTO STRICT v_rate_dir FROM public.exchange_rates WHERE target = i_item_id AND source = i_other_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
|
|
||||||
IF v_rate_dir = 'target_to_source'::public.exchange_rate_direction THEN
|
|
||||||
RETURN v_rate;
|
|
||||||
ELSE
|
|
||||||
RETURN 1/v_rate;
|
|
||||||
END IF;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN NO_DATA_FOUND THEN
|
|
||||||
RETURN -1;
|
|
||||||
END;
|
|
||||||
$$
|
|
||||||
|
|
||||||
|
|
||||||
-- return negative number on rate not found
|
|
||||||
CREATE OR REPLACE FUNCTION public.find_sell_rate(
|
|
||||||
IN i_item_id public.accounts.id%TYPE;
|
|
||||||
IN i_other_id public.accounts.id%TYPE;
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_rate public.exchange_rates.rate%TYPE;
|
|
||||||
v_rate_dir public.exchange_rates.rate_dir%TYPE;
|
|
||||||
BEGIN
|
|
||||||
SELECT rate INTO STRICT v_rate, rate_dir INTO STRICT v_rate_dir FROM public.exchange_rates WHERE target = i_other_id AND source = i_item_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
|
|
||||||
IF v_rate_dir = 'source_to_target'::public.exchange_rate_direction THEN
|
|
||||||
RETURN v_rate;
|
|
||||||
ELSE
|
|
||||||
RETURN 1/v_rate;
|
|
||||||
END IF;
|
|
||||||
EXCEPTION
|
|
||||||
WHEN NO_DATA_FOUND THEN
|
|
||||||
RETURN -1;
|
|
||||||
END;
|
|
||||||
$$
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.create_transaction(
|
|
||||||
i_responsible_id public.accounts.id%TYPE,
|
|
||||||
i_description public.transactions.description%TYPE
|
|
||||||
) RETURNS public.transactions.id%TYPE AS $$
|
|
||||||
DECLARE
|
|
||||||
new_transaction_id public.transactions%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
INSERT INTO public.transactions (responsible, description)
|
|
||||||
VALUES (i_responsible_id, i_description)
|
|
||||||
RETURNING id INTO new_transaction_id;
|
|
||||||
-- Return the new transaction ID
|
|
||||||
RETURN new_transaction_id;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.sell_item(
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_buy_rate NUMERIC;
|
|
||||||
v_sell_rate NUMERIC;
|
|
||||||
v_cost NUMERIC;
|
|
||||||
v_profit NUMERIC;
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Get the buy and sell rates from the stored functions
|
|
||||||
v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
|
|
||||||
v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
|
|
||||||
|
|
||||||
-- Calculate cost and profit
|
|
||||||
v_cost := i_amount * v_sell_rate;
|
|
||||||
v_profit := i_amount * (v_sell_rate - v_buy_rate);
|
|
||||||
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := public.create_transaction(i_user_id, i_description);
|
|
||||||
|
|
||||||
-- the item (decrease stock)
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_item_id, i_amount,
|
|
||||||
(SELECT "name" FROM public.accounts WHERE id = i_user_id));
|
|
||||||
|
|
||||||
-- the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_user_id, v_cost,
|
|
||||||
(SELECT "name" FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- the profit
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- Return the cost
|
|
||||||
RETURN v_cost;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(9);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,143 +0,0 @@
|
||||||
--
|
|
||||||
-- 0010-shop-sell-for-cash.sql
|
|
||||||
--
|
|
||||||
-- #10 - stored function for cash sell transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(9) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.create_transaction(
|
|
||||||
i_responsible_id public.accounts.id%TYPE,
|
|
||||||
i_description public.transactions.description%TYPE
|
|
||||||
) RETURNS public.transactions.id%TYPE AS $$
|
|
||||||
DECLARE
|
|
||||||
new_transaction_id public.transactions%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
INSERT INTO public.transactions (responsible, description)
|
|
||||||
VALUES (i_responsible_id, i_description)
|
|
||||||
RETURNING id INTO new_transaction_id;
|
|
||||||
-- Return the new transaction ID
|
|
||||||
RETURN new_transaction_id;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.sell_item_internal(
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_other_memo TEXT,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_buy_rate NUMERIC;
|
|
||||||
v_sell_rate NUMERIC;
|
|
||||||
v_cost NUMERIC;
|
|
||||||
v_profit NUMERIC;
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Get the buy and sell rates from the stored functions
|
|
||||||
v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
|
|
||||||
v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
|
|
||||||
|
|
||||||
-- Calculate cost and profit
|
|
||||||
v_cost := i_amount * v_sell_rate;
|
|
||||||
v_profit := i_amount * (v_sell_rate - v_buy_rate);
|
|
||||||
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(i_user_id, i_description);
|
|
||||||
|
|
||||||
-- the item (decrease stock)
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_item_id, i_amount,
|
|
||||||
i_other_memo);
|
|
||||||
|
|
||||||
-- the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_user_id, v_cost,
|
|
||||||
(SELECT "name" FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- the profit
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- Return the cost
|
|
||||||
RETURN v_cost;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.sell_item(
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
RETURN brmbar_privileged.sell_item_internal(i_item_id,
|
|
||||||
i_amount,
|
|
||||||
i_other_id,
|
|
||||||
i_target_currency_id,
|
|
||||||
(SELECT "name" FROM public.accounts WHERE id = i_user_id),
|
|
||||||
i_description);
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.sell_item_for_cash(
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
RETURN brmbar_privileged.sell_item_internal(i_item_id,
|
|
||||||
i_amount,
|
|
||||||
i_other_id,
|
|
||||||
i_target_currency_id,
|
|
||||||
'Cash',
|
|
||||||
i_description);
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
DROP FUNCTION public.create_transaction;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(10);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,102 +0,0 @@
|
||||||
--
|
|
||||||
-- 0011-shop-undo-sale.sql
|
|
||||||
--
|
|
||||||
-- #11 - stored function for sale undo transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(10) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.create_transaction(
|
|
||||||
i_responsible_id public.accounts.id%TYPE,
|
|
||||||
i_description public.transactions.description%TYPE
|
|
||||||
) RETURNS public.transactions.id%TYPE AS $$
|
|
||||||
DECLARE
|
|
||||||
new_transaction_id public.transactions%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
INSERT INTO public.transactions (responsible, description)
|
|
||||||
VALUES (i_responsible_id, i_description)
|
|
||||||
RETURNING id INTO new_transaction_id;
|
|
||||||
-- Return the new transaction ID
|
|
||||||
RETURN new_transaction_id;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.undo_sale_of_item(
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_buy_rate NUMERIC;
|
|
||||||
v_sell_rate NUMERIC;
|
|
||||||
v_cost NUMERIC;
|
|
||||||
v_profit NUMERIC;
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Get the buy and sell rates from the stored functions
|
|
||||||
v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
|
|
||||||
v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
|
|
||||||
|
|
||||||
-- Calculate cost and profit
|
|
||||||
v_cost := i_amount * v_sell_rate;
|
|
||||||
v_profit := i_amount * (v_sell_rate - v_buy_rate);
|
|
||||||
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(i_user_id, i_description);
|
|
||||||
|
|
||||||
-- the item (decrease stock)
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_item_id, i_amount,
|
|
||||||
(SELECT "name" || ' (sale undo)' FROM public.accounts WHERE id = i_user_id));
|
|
||||||
|
|
||||||
-- the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_user_id, v_cost,
|
|
||||||
(SELECT "name" || ' (sale undo)' FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- the profit
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin repaid on ' || "name" FROM public.accounts WHERE id = i_item_id));
|
|
||||||
|
|
||||||
-- Return the cost
|
|
||||||
RETURN v_cost;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(11);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,64 +0,0 @@
|
||||||
--
|
|
||||||
-- 0012-shop-add-credit.sql
|
|
||||||
--
|
|
||||||
-- #12 - stored function for cash deposit transactions
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(11) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.add_credit(
|
|
||||||
i_cash_account_id public.accounts.id%TYPE,
|
|
||||||
i_credit NUMERIC,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_user_name TEXT
|
|
||||||
) RETURNS VOID
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(i_user_id, 'BrmBar credit replenishment for ' || i_user_name);
|
|
||||||
-- Debit cash (credit replenishment)
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (v_transaction_id, 'debit', i_cash_account_id, i_credit, i_user_name);
|
|
||||||
-- Credit the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (v_transaction_id, 'credit', i_user_id, i_credit, 'Credit replenishment');
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(12);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,64 +0,0 @@
|
||||||
--
|
|
||||||
-- 0013-shop-withdraw-credit.sql
|
|
||||||
--
|
|
||||||
-- #13 - stored function for cash withdrawal transactions
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(12) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.withdraw_credit(
|
|
||||||
i_cash_account_id public.accounts.id%TYPE,
|
|
||||||
i_credit NUMERIC,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_user_name TEXT
|
|
||||||
) RETURNS VOID
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(i_user_id, 'BrmBar credit withdrawal for ' || i_user_name);
|
|
||||||
-- Debit cash (credit replenishment)
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (v_transaction_id, 'credit', i_cash_account_id, i_credit, i_user_name);
|
|
||||||
-- Credit the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (v_transaction_id, 'debit', i_user_id, i_credit, 'Credit withdrawal');
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(13);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,58 +0,0 @@
|
||||||
--
|
|
||||||
-- 0014-shop-transfer-credit.sql
|
|
||||||
--
|
|
||||||
-- #14 - stored function for "credit" transfer transactions
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(13) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.transfer_credit(
|
|
||||||
i_cash_account_id public.accounts.id%TYPE,
|
|
||||||
i_credit NUMERIC,
|
|
||||||
i_userfrom_id public.accounts.id%TYPE,
|
|
||||||
i_userfrom_name TEXT,
|
|
||||||
i_userto_id public.accounts.id%TYPE,
|
|
||||||
i_userto_name TEXT
|
|
||||||
) RETURNS VOID
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
PERFORM public.add_credit(i_cash_account_id, i_credit, i_userto_id, i_userto_name);
|
|
||||||
PERFORM public.withdraw_credit(i_cash_account_id, i_credit, i_userfrom_id, i_userfrom_name);
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(14);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,82 +0,0 @@
|
||||||
--
|
|
||||||
-- 0015-shop-buy-for-cash.sql
|
|
||||||
--
|
|
||||||
-- #15 - stored function for cash-based stock replenishment transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(14) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.buy_for_cash(
|
|
||||||
i_cash_account_id public.accounts.id%TYPE,
|
|
||||||
i_item_id public.accounts.id%TYPE,
|
|
||||||
i_amount INTEGER,
|
|
||||||
i_target_currency_id public.currencies.id%TYPE,
|
|
||||||
i_item_name TEXT
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_buy_rate NUMERIC;
|
|
||||||
v_cost NUMERIC;
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- this could fail and it would generate exception in python
|
|
||||||
-- FIXME: convert v_buy_rate < 0 into python exception
|
|
||||||
v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
|
|
||||||
-- this could fail and it would generate exception in python, even though it is not used
|
|
||||||
--v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
|
|
||||||
|
|
||||||
-- Calculate cost and profit
|
|
||||||
v_cost := i_amount * v_buy_rate;
|
|
||||||
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(NULL,
|
|
||||||
'BrmBar stock replenishment of ' || i_amount || 'x ' || i_item_name || ' for cash');
|
|
||||||
|
|
||||||
-- the item
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_item_id, i_amount,
|
|
||||||
'Cash');
|
|
||||||
|
|
||||||
-- the cash
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_cash_account_id, v_cost,
|
|
||||||
i_item_name);
|
|
||||||
|
|
||||||
-- Return the cost
|
|
||||||
RETURN v_cost;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(15);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,64 +0,0 @@
|
||||||
--
|
|
||||||
-- 0016-shop-buy-for-cash.sql
|
|
||||||
--
|
|
||||||
-- #16 - stored function for receipt reimbursement transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(15) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.receipt_reimbursement(
|
|
||||||
i_profits_id public.accounts.id%TYPE,
|
|
||||||
i_user_id public.accounts.id%TYPE,
|
|
||||||
i_user_name public.accounts.name%TYPE,
|
|
||||||
i_amount NUMERIC,
|
|
||||||
i_description TEXT
|
|
||||||
) RETURNS VOID
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
BEGIN
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(i_user_id,
|
|
||||||
'Receipt: ' || i_description);
|
|
||||||
-- the "profit"
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_profits_id, i_amount, i_user_name);
|
|
||||||
-- the user
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_user_id, i_amount, 'Credit from receipt: ' || i_description);
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(16);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,157 +0,0 @@
|
||||||
--
|
|
||||||
-- 0017-shop-fix-inventory.sql
|
|
||||||
--
|
|
||||||
-- #17 - stored function for "fixing" inventory transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(16) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.compute_account_balance(
|
|
||||||
i_account_id public.accounts.id%TYPE
|
|
||||||
) RETURNS NUMERIC
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_crsum NUMERIC;
|
|
||||||
v_dbsum NUMERIC;
|
|
||||||
BEGIN
|
|
||||||
SELECT COALESCE(SUM(CASE WHEN side='credit' THEN amount ELSE 0 END),0) crsum INTO v_crsum,
|
|
||||||
COALESCE(SUM(CASE WHEN side='debit' THEN amount ELSE 0 END),0) dbsum into v_dbsum
|
|
||||||
FROM public.transaction_splits ts WHERE ts.account=4
|
|
||||||
RETURN v_dbsum - v_crsum;
|
|
||||||
END; $$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION brmbar_privileged.fix_account_balance(
|
|
||||||
IN i_account_id public.acounts.id%TYPE,
|
|
||||||
IN i_account_currency_id public.currencies.id%TYPE,
|
|
||||||
IN i_excess_id public.acounts.id%TYPE,
|
|
||||||
IN i_deficit_id public.acounts.id%TYPE,
|
|
||||||
IN i_shop_currency_id public.currencies.id%TYPE,
|
|
||||||
IN i_amount_in_reality NUMERIC
|
|
||||||
) RETURNS BOOLEAN
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
|
|
||||||
DECLARE
|
|
||||||
v_amount_in_system NUMERIC;
|
|
||||||
v_buy_rate NUMERIC;
|
|
||||||
v_currency_id public.currencies.id%TYPE;
|
|
||||||
v_diff NUMERIC;
|
|
||||||
v_buy_total NUMERIC;
|
|
||||||
v_ntrn_id public.transactions.id%TYPE;
|
|
||||||
v_transaction_memo TEXT;
|
|
||||||
v_item_name TEXT;
|
|
||||||
v_excess_memo TEXT;
|
|
||||||
v_deficit_memo TEXT;
|
|
||||||
|
|
||||||
v_old_trn public.transactions%ROWTYPE;
|
|
||||||
v_old_split public.transaction_splits%ROWTYPE;
|
|
||||||
BEGIN
|
|
||||||
v_amount_in_system := public.compute_account_balance(i_account_id);
|
|
||||||
IF i_account_currency_id <> i_shop_currency_id THEN
|
|
||||||
v_buy_rate := public.find_buy_rate(i_item_id, i_shop_currency_id);
|
|
||||||
ELSE
|
|
||||||
v_buy_rate := 1;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
v_diff := ABS(i_amount_in_reality - v_amount_in_system);
|
|
||||||
v_buy_total := v_buy_rate * v_diff;
|
|
||||||
-- compute memo strings
|
|
||||||
IF i_item_id = 1 THEN -- cash account recognized by magic id
|
|
||||||
-- fixing cash
|
|
||||||
v_transaction_memo :=
|
|
||||||
'BrmBar cash inventory fix of ' || v_amount_in_system
|
|
||||||
|| ' in system to ' || i_amount_in_reality || ' in reality';
|
|
||||||
v_excess_memo := 'Inventory cash fix excess.';
|
|
||||||
v_deficit_memo := 'Inventory fix deficit.';
|
|
||||||
ELSE
|
|
||||||
-- fixing other account
|
|
||||||
SELECT "name" INTO v_item_name FROM public.accounts WHERE id = i_account_id;
|
|
||||||
v_transaction_memo :=
|
|
||||||
'BrmBar inventory fix of ' || v_amount_in_system || 'pcs '
|
|
||||||
|| v_item_name
|
|
||||||
|| ' in system to ' || i_amount_in_reality || 'pcs in reality';
|
|
||||||
v_excess_memo := 'Inventory fix excess ' || v_item_name;
|
|
||||||
v_deficit_memo := 'Inventory fix deficit ' || v_item_name;
|
|
||||||
END IF;
|
|
||||||
-- create transaction based on the relation between counting and accounting
|
|
||||||
IF i_amount_in_reality > v_amount_in_system THEN
|
|
||||||
v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'debit', i_item_id, v_diff, 'Inventory fix excess');
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'credit', i_excess_id, v_buy_total, v_excess_memo);
|
|
||||||
RETURN TRUE;
|
|
||||||
ELSIF i_amount_in_reality < v_amount_in_system THEN
|
|
||||||
v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'credit', i_item_id, v_diff, 'Inventory fix deficit');
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'debit', i_deficit_id, v_buy_total, v_deficit_memo);
|
|
||||||
RETURN TRUE;
|
|
||||||
ELSIF i_account_id <> 1 THEN -- cash account recognized by magic id
|
|
||||||
-- record that everything is going on swimmingly only for noncash accounts (WTF)
|
|
||||||
v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'debit', i_item_id, 0, 'Inventory fix - amount was correct');
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, 'credit', i_item_id, 0, 'Inventory fix - amount was correct');
|
|
||||||
RETURN FALSE;
|
|
||||||
END IF;
|
|
||||||
RETURN FALSE;
|
|
||||||
END;
|
|
||||||
$fn$;
|
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.fix_inventory(
|
|
||||||
IN i_account_id public.acounts.id%TYPE,
|
|
||||||
IN i_account_currency_id public.currencies.id%TYPE,
|
|
||||||
IN i_excess_id public.acounts.id%TYPE,
|
|
||||||
IN i_deficit_id public.acounts.id%TYPE,
|
|
||||||
IN i_shop_currency_id public.currencies.id%TYPE,
|
|
||||||
IN i_amount_in_reality NUMERIC
|
|
||||||
) RETURNS BOOLEAN
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
|
|
||||||
BEGIN
|
|
||||||
RETURN brmbar_privileged.fix_account_balance(
|
|
||||||
i_account_id,
|
|
||||||
i_account_currency_id,
|
|
||||||
i_excess_id,
|
|
||||||
i_deficit_id,
|
|
||||||
i_shop_currency_id,
|
|
||||||
i_amount_in_reality
|
|
||||||
);
|
|
||||||
END;
|
|
||||||
$fn$;
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(17);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,60 +0,0 @@
|
||||||
--
|
|
||||||
-- 0018-shop-fix-cash.sql
|
|
||||||
--
|
|
||||||
-- #18 - stored function for "fixing cash" transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(17) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.fix_cash(
|
|
||||||
IN i_excess_id public.acounts.id%TYPE,
|
|
||||||
IN i_deficit_id public.acounts.id%TYPE,
|
|
||||||
IN i_shop_currency_id public.currencies.id%TYPE,
|
|
||||||
IN i_amount_in_reality NUMERIC
|
|
||||||
) RETURNS BOOLEAN
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
|
|
||||||
BEGIN
|
|
||||||
RETURN brmbar_privileged.fix_account_balance(
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
i_excess_id,
|
|
||||||
i_deficit_id,
|
|
||||||
i_shop_currency_id,
|
|
||||||
i_amount_in_reality
|
|
||||||
);
|
|
||||||
END;
|
|
||||||
$fn$;
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(18);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,82 +0,0 @@
|
||||||
--
|
|
||||||
-- 0019-shop-consolidate.sql
|
|
||||||
--
|
|
||||||
-- #19 - stored function for "consolidation" transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(18) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.make_consolidate_transaction(
|
|
||||||
i_excess_id public.accounts.id%TYPE,
|
|
||||||
i_deficit_id public.accounts.id%TYPE,
|
|
||||||
i_profits_id public.accounts.id%TYPE
|
|
||||||
) RETURNS TEXT
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
v_transaction_id public.transactions.id%TYPE;
|
|
||||||
v_excess_balance NUMERIC;
|
|
||||||
v_deficit_balance NUMERIC;
|
|
||||||
v_ret TEXT;
|
|
||||||
BEGIN
|
|
||||||
v_ret := NULL;
|
|
||||||
-- Create a new transaction
|
|
||||||
v_transaction_id := brmbar_privileged.create_transaction(NULL,
|
|
||||||
'BrmBar inventory consolidation');
|
|
||||||
v_excess_balance := public.compute_account_balance(i_excess_id);
|
|
||||||
v_deficit_balance := public.compute_account_balance(i_deficit_id);
|
|
||||||
IF v_excess_balance <> 0 THEN
|
|
||||||
v_ret := 'Excess balance ' || -v_excess_balance || ' debited to profit';
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_excess_id, -v_excess_balance,
|
|
||||||
'Excess balance added to profit.');
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'debit', i_profits_id, -v_excess_balance,
|
|
||||||
'Excess balance added to profit.');
|
|
||||||
END IF;
|
|
||||||
IF v_deficit_balance <> 0 THEN
|
|
||||||
v_ret := COALESCE(v_ret, '');
|
|
||||||
v_ret := v_ret || 'Deficit balance ' || v_deficit_balance || ' credited to profit';
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_deficit_id, v_deficit_balance,
|
|
||||||
'Deficit balance removed from profit.');
|
|
||||||
INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
|
|
||||||
VALUES (i_transaction_id, 'credit', i_profits_id, v_deficit_balance,
|
|
||||||
'Deficit balance removed from profit.');
|
|
||||||
END IF;
|
|
||||||
RETURN v_ret;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(19);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,64 +0,0 @@
|
||||||
--
|
|
||||||
-- 0020-shop-undo.sql
|
|
||||||
--
|
|
||||||
-- #20 - stored function for undo transaction
|
|
||||||
--
|
|
||||||
-- ISC License
|
|
||||||
--
|
|
||||||
-- Copyright 2023-2025 Brmlab, z.s.
|
|
||||||
-- TMA <tma+hs@jikos.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);
|
|
||||||
|
|
||||||
DO $upgrade_block$
|
|
||||||
BEGIN
|
|
||||||
|
|
||||||
IF brmbar_privileged.has_exact_schema_version(19) THEN
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION public.undo_transaction(
|
|
||||||
IN i_id public.transactions.id%TYPE)
|
|
||||||
RETURNS public.transactions.id%TYPE
|
|
||||||
VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
|
|
||||||
DECLARE
|
|
||||||
v_ntrn_id public.transactions.id%TYPE;
|
|
||||||
v_old_trn public.transactions%ROWTYPE;
|
|
||||||
v_old_split public.transaction_splits%ROWTYPE;
|
|
||||||
BEGIN
|
|
||||||
SELECT * INTO v_old_trn FROM public.transactions WHERE id = i_id;
|
|
||||||
INSERT INTO transactions ("description") VALUES ('undo '||o_id||' ('||v_old_trn.description||')') RETURNING id into v_ntrn_id;
|
|
||||||
FOR v_old_split IN
|
|
||||||
SELECT * FROM transaction_splits WHERE "transaction" = i_id
|
|
||||||
LOOP
|
|
||||||
INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
|
|
||||||
VALUES (v_ntrn_id, v_old_split.side, v_old_split.account, -v_old_split.amount,
|
|
||||||
'undo ' || v_old_split.id || ' (' || v_old_split.memo || ')' );
|
|
||||||
END LOOP;
|
|
||||||
RETURN v_ntrn_id;
|
|
||||||
END;
|
|
||||||
$fn$;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PERFORM brmbar_privileged.upgrade_schema_version_to(20);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END;
|
|
||||||
$upgrade_block$;
|
|
||||||
|
|
||||||
-- vim: set ft=plsql :
|
|
|
@ -1,73 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
#from brmbar import Database
|
|
||||||
#from brmbar import Currency
|
|
||||||
|
|
||||||
from contextlib import closing
|
|
||||||
import psycopg2
|
|
||||||
from brmbar.Database import Database
|
|
||||||
from brmbar.Currency import Currency
|
|
||||||
import math
|
|
||||||
|
|
||||||
#import brmbar
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def approx_equal(a, b, tol=1e-6):
|
|
||||||
"""Check if two (buy, sell) rate tuples are approximately equal."""
|
|
||||||
return (
|
|
||||||
isinstance(a, tuple) and isinstance(b, tuple) and
|
|
||||||
math.isclose(a[0], b[0], abs_tol=tol) and
|
|
||||||
math.isclose(a[1], b[1], abs_tol=tol)
|
|
||||||
)
|
|
||||||
|
|
||||||
def compare_exceptions(e1, e2):
|
|
||||||
"""Compare exception types and messages."""
|
|
||||||
return type(e1) == type(e2) and str(e1) == str(e2)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
db = Database("dbname=brmbar")
|
|
||||||
|
|
||||||
# Get all currencies
|
|
||||||
with closing(db.db_conn.cursor()) as cur:
|
|
||||||
cur.execute("SELECT id, name FROM currencies")
|
|
||||||
currencies = cur.fetchall()
|
|
||||||
|
|
||||||
# Build Currency objects
|
|
||||||
currency_objs = [Currency(db, id, name) for id, name in currencies]
|
|
||||||
|
|
||||||
# Test all currency pairs
|
|
||||||
for c1 in currency_objs:
|
|
||||||
for c2 in currency_objs:
|
|
||||||
#if c1.id == c2.id:
|
|
||||||
# continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
rates1 = c1.rates(c2)
|
|
||||||
exc1 = None
|
|
||||||
except (RuntimeError, NameError) as e1:
|
|
||||||
rates1 = None
|
|
||||||
exc1 = e1
|
|
||||||
|
|
||||||
try:
|
|
||||||
rates2 = c1.rates2(c2)
|
|
||||||
exc2 = None
|
|
||||||
except (RuntimeError, NameError) as e2:
|
|
||||||
rates2 = None
|
|
||||||
exc2 = e2
|
|
||||||
|
|
||||||
if exc1 or exc2:
|
|
||||||
if not compare_exceptions(exc1, exc2):
|
|
||||||
print(f"[EXCEPTION DIFFERENCE] {c1.name} -> {c2.name}")
|
|
||||||
print(f" rates() exception: {type(exc1).__name__}: {exc1}")
|
|
||||||
print(f" rates2() exception: {type(exc2).__name__}: {exc2}")
|
|
||||||
elif not approx_equal(rates1, rates2):
|
|
||||||
print(f"[VALUE DIFFERENCE] {c1.name} -> {c2.name}")
|
|
||||||
print(f" rates(): {rates1}")
|
|
||||||
print(f" rates2(): {rates2}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ `./brmbar-cli.py iteminfo uklid|grep -o '[0-9]*.[0-9]* pcs'|cut -d '.' -f 1` -eq 0 ]; then
|
|
||||||
BOUNTY=`./brmbar-cli.py restock uklid 1 | grep -o 'take -[0-9]*'|grep -o '[0-9]*'`
|
|
||||||
echo "Brmlab cleanup bounty for ${BOUNTY}CZK!!!"|ssh jenda@fry.hrach.eu
|
|
||||||
fi
|
|
|
@ -1,18 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
LASTIDF=/home/brmlab/uklid.last
|
|
||||||
|
|
||||||
LASTID=`cat $LASTIDF 2>/dev/null || echo 0`
|
|
||||||
|
|
||||||
|
|
||||||
RES=`psql brmbar -Atq -c "select id,description from transactions where id>$LASTID and description like 'BrmBar sale of 1x uklid%' LIMIT 1;"`
|
|
||||||
if [ ! -z "$RES" ]; then
|
|
||||||
LASTID=`echo "$RES"|cut -d '|' -f 1`
|
|
||||||
echo $LASTID > $LASTIDF
|
|
||||||
|
|
||||||
WINNER=`echo "$RES"|grep -o 'to [^ ]*'|cut -d ' ' -f 2`
|
|
||||||
if [ -z "$WINNER" ]; then
|
|
||||||
WINNER="anonymous hunter"
|
|
||||||
fi
|
|
||||||
echo "Brmlab cleanup bounty was claimed by $WINNER! Thanks!"|ssh -p 110 jenda@coralmyn.hrach.eu
|
|
||||||
fi
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue