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?
|
||||
*~
|
||||
|
|
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__
|
||||
*.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 (
|
||||
id INTEGER PRIMARY KEY NOT NULL DEFAULT NEXTVAL('currencies_id_seq'::regclass),
|
||||
name VARCHAR(128) NOT NULL,
|
||||
UNIQUE(name)
|
||||
);
|
||||
-- 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 TABLE exchange_rates (
|
||||
|
@ -39,7 +39,8 @@ CREATE TABLE accounts (
|
|||
|
||||
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 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
|
||||
-- have balances in stock amounts.
|
||||
CREATE VIEW account_balances AS
|
||||
SELECT ts.account AS id, accounts.name AS name, accounts.acctype AS acctype,
|
||||
-SUM(CASE WHEN ts.side = 'credit' THEN -ts.amount ELSE ts.amount END) AS crbalance
|
||||
FROM transaction_splits AS ts
|
||||
LEFT JOIN accounts ON accounts.id = ts.account
|
||||
GROUP BY ts.account, accounts.name, accounts.acctype
|
||||
ORDER BY crbalance ASC;
|
||||
SELECT id, name, acctype, crbalance FROM accounts ORDER BY crbalance ASC;
|
||||
|
||||
-- SELECT ts.account AS id, accounts.name AS name, accounts.acctype AS acctype,
|
||||
-- -SUM(CASE WHEN ts.side = 'credit' THEN -ts.amount ELSE ts.amount END) AS crbalance
|
||||
-- FROM transaction_splits AS ts
|
||||
-- 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
|
||||
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
|
||||
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
|
||||
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
|
||||
LEFT JOIN (SELECT cts.amount AS credit_cash, cts.transaction AS cts_t
|
||||
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')
|
||||
AND a.acctype IN ('cash', 'debt')
|
||||
AND dts.amount > 0) debit ON dts_t = t.id
|
||||
LEFT JOIN accounts AS a ON a.id = t.responsible
|
||||
GROUP BY t.id, a.name ORDER BY t.id DESC;
|
||||
GROUP BY t.id ORDER BY t.id;
|
||||
|
|
|
@ -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).
|
||||
|
||||
* If your inventory stock count or cash box amount does not match
|
||||
the in-system data, you will need to make a corrective transaction.
|
||||
To fix cash amount to reality in which you counted 1234Kč, use
|
||||
|
||||
./brmbar-cli.py fixcash 1234
|
||||
|
||||
whereas to fix amount of a particular stock, use
|
||||
|
||||
./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.
|
||||
the in-system data, you will need to make a corrective transaction.
|
||||
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
|
||||
way to do this from the accounting standpoint. In the meantime, you
|
||||
can use SQL INSERTs to manually create a transaction with appropriate
|
||||
transaction splits (see doc/architecture for details on splits).
|
||||
|
||||
|
||||
Useful SQL queries
|
||||
|
@ -122,32 +81,32 @@ Useful SQL queries
|
|||
|
||||
* Compute sum of sold stock:
|
||||
|
||||
select sum(amount) from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where description like '% sale %' and side = 'debit';
|
||||
select sum(amount) from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where description like '% sale %' and side = 'debit';
|
||||
|
||||
* List of items not covered by inventory check:
|
||||
|
||||
select * from account_balances
|
||||
where id not in (select account from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where description like '% inventory %')
|
||||
and acctype = 'inventory';
|
||||
select * from account_balances
|
||||
where id not in (select account from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where description like '% inventory %')
|
||||
and acctype = 'inventory';
|
||||
|
||||
* List all cash transactions:
|
||||
|
||||
select time, transactions.id, description, responsible, amount from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where transaction_splits.account = 1;
|
||||
select time, transactions.id, description, responsible, amount from transactions
|
||||
left join transaction_splits on transaction_splits.transaction = transactions.id
|
||||
where transaction_splits.account = 1;
|
||||
|
||||
* List all inventory items ordered by their cummulative worth:
|
||||
|
||||
select foo.*, foo.rate * -foo.crbalance as worth from
|
||||
(select account_balances.*,
|
||||
(select exchange_rates.rate from exchange_rates, accounts
|
||||
where exchange_rates.target = accounts.currency
|
||||
and accounts.id = account_balances.id
|
||||
order by exchange_rates.valid_since limit 1) as rate
|
||||
from account_balances where account_balances.acctype = 'inventory')
|
||||
as foo order by worth;
|
||||
select foo.*, foo.rate * -foo.crbalance as worth from
|
||||
(select account_balances.*,
|
||||
(select exchange_rates.rate from exchange_rates, accounts
|
||||
where exchange_rates.target = accounts.currency
|
||||
and accounts.id = account_balances.id
|
||||
order by exchange_rates.valid_since limit 1) as rate
|
||||
from account_balances where account_balances.acctype = 'inventory')
|
||||
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
|
||||
showcredit USER
|
||||
changecredit USER +-AMT
|
||||
sellitem {USER|"cash"} ITEM +-AMT
|
||||
sellitem USER ITEM +-AMT
|
||||
You can use negative AMT to undo a sale.
|
||||
restock ITEM AMT
|
||||
userinfo USER
|
||||
userlog USER TIMESTAMP
|
||||
iteminfo ITEM
|
||||
|
||||
2. Management commands
|
||||
|
@ -32,21 +30,15 @@ Usage: brmbar-cli.py COMMAND ARGS...
|
|||
screen of the GUI.
|
||||
adduser USER
|
||||
Add user (debt) account with given username.
|
||||
undo TRANSID
|
||||
Commit a transaction that reverses all splits of a transaction with
|
||||
a given id (to find out that id: select * from transaction_cashsums;)
|
||||
|
||||
3. Inventorization
|
||||
|
||||
inventory ITEM1 NEW_AMOUNT1 ITEM2 NEW_AMOUNT2
|
||||
Inventory recounting (fixing the number of items)
|
||||
inventory-interactive
|
||||
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
|
||||
inventory ITEM1 NEW_AMOUNT1 ITEM2 NEW_AMOUNT2
|
||||
Inventory recounting (fixing the number of items)
|
||||
inventory-interactive
|
||||
Launches interactive mode for performing inventory with barcode reader
|
||||
changecash 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
|
||||
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
|
||||
to find out the item id.
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
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
|
||||
""")
|
||||
Commands prefixed with ! are not implemented yet.""")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
@ -97,12 +77,6 @@ def load_item(inp):
|
|||
exit(1)
|
||||
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")
|
||||
shop = brmbar.Shop.new_with_defaults(db)
|
||||
|
@ -126,20 +100,14 @@ elif sys.argv[1] == "changecredit":
|
|||
print("{}: {}".format(acct.name, acct.negbalance_str()))
|
||||
|
||||
elif sys.argv[1] == "sellitem":
|
||||
if sys.argv[2] == "cash":
|
||||
uacct = shop.cash
|
||||
else:
|
||||
uacct = load_user(sys.argv[2])
|
||||
uacct = load_user(sys.argv[2])
|
||||
iacct = load_item(sys.argv[3])
|
||||
amt = int(sys.argv[4])
|
||||
if amt > 0:
|
||||
if uacct == shop.cash:
|
||||
shop.sell_for_cash(item = iacct, amount = amt)
|
||||
else:
|
||||
shop.sell(item = iacct, user = uacct, amount = amt)
|
||||
shop.sell(item = iacct, user = uacct, amount = amt)
|
||||
elif amt < 0:
|
||||
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()))
|
||||
|
||||
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])
|
||||
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":
|
||||
acct = load_item(sys.argv[2])
|
||||
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()))
|
||||
|
||||
elif sys.argv[1] == "stats":
|
||||
print("--- Material Assets ---")
|
||||
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("Fixups: {} (excess {}, deficit {})".format(
|
||||
-shop.excess.balance() - shop.deficit.balance(),
|
||||
shop.excess.negbalance_str(),
|
||||
shop.deficit.balance_str()))
|
||||
print("Credit: {}".format(shop.credit_negbalance_str()))
|
||||
print("Inventory: {}".format(shop.inventory_balance_str()))
|
||||
print("Excess: {}".format(shop.excess.negbalance_str()))
|
||||
print("Deficit: {}".format(shop.deficit.balance_str()))
|
||||
|
||||
elif sys.argv[1] == "adduser":
|
||||
acct = brmbar.Account.create(db, sys.argv[2], brmbar.Currency.load(db, id = 1), 'debt')
|
||||
acct.add_barcode(sys.argv[2]) # will commit
|
||||
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":
|
||||
if (len(sys.argv) % 2 != 0 or len(sys.argv) < 4):
|
||||
print ("Invalid number of parameters, count your parameters.")
|
||||
|
@ -214,27 +165,26 @@ elif sys.argv[1] == "inventory":
|
|||
elif sys.argv[1] == "inventory-interactive":
|
||||
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:"))
|
||||
fuckyou = input("fuckyou")
|
||||
if barcode == "":
|
||||
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:
|
||||
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()))
|
||||
iacct = brmbar.Account.load_by_barcode(db, barcode)
|
||||
amount = str(input("What is the amount of {} in reality current is {}:".format(iacct.name, iacct.balance())))
|
||||
if amount == "":
|
||||
break
|
||||
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")
|
||||
|
||||
elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash":
|
||||
elif sys.argv[1] == "changecash":
|
||||
if (len(sys.argv) != 3):
|
||||
print ("Invalid number of parameters, check your parameters.")
|
||||
else:
|
||||
|
@ -244,23 +194,12 @@ elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash":
|
|||
print("New Cash is : {}".format(shop.cash.balance_str()))
|
||||
else:
|
||||
print ("No action needed amount is the same.")
|
||||
|
||||
elif sys.argv[1] == "consolidate":
|
||||
if (len(sys.argv) != 2):
|
||||
print ("Invalid number of parameters, check your parameters.")
|
||||
else:
|
||||
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:
|
||||
help()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from PySide import QtCore, QtGui, QtDeclarative
|
||||
|
||||
|
@ -9,15 +8,6 @@ from brmbar import Database
|
|||
|
||||
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):
|
||||
""" Interface between QML and the brmbar package """
|
||||
|
@ -39,14 +29,6 @@ class ShopAdapter(QtCore.QObject):
|
|||
map["price"] = str(sell)
|
||||
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):
|
||||
map = acct.__dict__.copy()
|
||||
return map
|
||||
|
@ -88,18 +70,6 @@ class ShopAdapter(QtCore.QObject):
|
|||
db.commit()
|
||||
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')
|
||||
def sellItem(self, itemid, userid):
|
||||
user = brmbar.Account.load(db, id = userid)
|
||||
|
@ -115,7 +85,6 @@ class ShopAdapter(QtCore.QObject):
|
|||
|
||||
@QtCore.Slot('QVariant', 'QVariant', result='QVariant')
|
||||
def chargeCredit(self, credit, userid):
|
||||
subprocess.call(["sh", ALERT_SCRIPT, "charge"])
|
||||
user = brmbar.Account.load(db, id = userid)
|
||||
shop.add_credit(credit = credit, user = user)
|
||||
balance = user.negbalance_str()
|
||||
|
@ -130,19 +99,6 @@ class ShopAdapter(QtCore.QObject):
|
|||
db.commit()
|
||||
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')
|
||||
def balance_cash(self):
|
||||
balance = shop.cash.balance_str()
|
||||
|
@ -172,7 +128,7 @@ class ShopAdapter(QtCore.QObject):
|
|||
|
||||
@QtCore.Slot('QVariant', result='QVariant')
|
||||
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()
|
||||
return alist
|
||||
|
||||
|
|
|
@ -100,17 +100,8 @@ Item {
|
|||
}
|
||||
|
||||
function chargeCredit() {
|
||||
var balance=0
|
||||
if (!isNaN(amount)) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
var balance = shop.chargeCredit(amount, userdbid)
|
||||
status_text.setStatus("Charged! "+username+"'s credit is "+balance+".", "#ffff7c")
|
||||
loadPage("MainPage")
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,8 +58,6 @@ Item {
|
|||
if (acct.acctype == "cash") { //Copied from BarButton.onButtonClick
|
||||
shop.sellItemCash(dbid)
|
||||
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 {
|
||||
var balance = shop.sellItem(dbid, acct.id)
|
||||
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 {
|
||||
id: management
|
||||
x: 855
|
||||
|
@ -53,11 +43,4 @@ Item {
|
|||
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() {
|
||||
var balance=0
|
||||
if (!isNaN(amount)) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
var balance = shop.withdrawCredit(amount, userdbid)
|
||||
status_text.setStatus("Withdrawn! "+username+"'s credit is "+balance+".", "#ffff7c")
|
||||
loadPage("MainPage")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
|
||||
|
|
|
@ -40,22 +40,14 @@ class Account:
|
|||
@classmethod
|
||||
def create(cls, db, name, currency, acctype):
|
||||
""" 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("SELECT public.create_account(%s, %s, %s)", [name, currency.id, acctype])
|
||||
# id = id[0]
|
||||
id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype])
|
||||
id = id[0]
|
||||
return cls(db, name = name, id = id, currency = currency, acctype = acctype)
|
||||
|
||||
def balance(self):
|
||||
bal = self.db.execute_and_fetch(
|
||||
"SELECT public.compute_account_balance(%s)",
|
||||
[self.id]
|
||||
)[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
|
||||
crbalance = self.db.execute_and_fetch("SELECT crbalance FROM accounts WHERE id = %s", [self.id])
|
||||
crbalance = crbalance[0] or 0
|
||||
return crbalance
|
||||
|
||||
def balance_str(self):
|
||||
return self.currency.str(self.balance())
|
||||
|
@ -69,16 +61,17 @@ class Account:
|
|||
def credit(self, transaction, amount, memo):
|
||||
return self._transaction_split(transaction, 'credit', amount, memo)
|
||||
|
||||
# XXX atomicita
|
||||
def _transaction_split(self, transaction, side, amount, memo):
|
||||
""" 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("UPDATE accounts set crbalance = crbalance + (CASE WHEN %s = 'credit' THEN -%s ELSE %s END)", [side,amount,amount])
|
||||
|
||||
def add_barcode(self, 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.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode])
|
||||
self.db.commit()
|
||||
|
||||
def rename(self, name):
|
||||
# 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.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id])
|
||||
self.name = name
|
||||
|
|
|
@ -31,34 +31,15 @@ class Currency:
|
|||
@classmethod
|
||||
def create(cls, db, name):
|
||||
""" Constructor for new currency """
|
||||
# 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 = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name])
|
||||
id = id[0]
|
||||
return cls(db, name = name, id = id)
|
||||
|
||||
def rates(self, other):
|
||||
""" 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)
|
||||
$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])
|
||||
if res is None:
|
||||
raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name())
|
||||
|
@ -73,7 +54,6 @@ class Currency:
|
|||
|
||||
return (buy, sell)
|
||||
|
||||
|
||||
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])
|
||||
if res is None:
|
||||
|
@ -89,10 +69,6 @@ class Currency:
|
|||
return "{:.2f} {}".format(amount, self.name)
|
||||
|
||||
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("SELECT public.update_currency_sell_rate(%s, %s, %s)",
|
||||
[self.id, target.id, 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"])
|
||||
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("SELECT public.update_currency_buy_rate(%s, %s, %s)",
|
||||
[source.id, self.id, 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"])
|
||||
|
|
|
@ -25,123 +25,75 @@ class Shop:
|
|||
deficit = Account.load(db, name = "BrmBar Deficit"))
|
||||
|
||||
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
|
||||
#(buy, sell) = item.currency.rates(self.currency)
|
||||
#cost = amount * sell
|
||||
#profit = amount * (sell - buy)
|
||||
(buy, sell) = item.currency.rates(self.currency)
|
||||
cost = amount * sell
|
||||
profit = amount * (sell - buy)
|
||||
|
||||
#transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name))
|
||||
#item.credit(transaction, amount, user.name)
|
||||
#user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account
|
||||
#self.profits.debit(transaction, profit, "Margin on " + item.name)
|
||||
#self.db.commit()
|
||||
#return cost
|
||||
transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name))
|
||||
item.credit(transaction, amount, user.name)
|
||||
user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account
|
||||
self.profits.debit(transaction, profit, "Margin on " + item.name)
|
||||
self.db.commit()
|
||||
|
||||
return cost
|
||||
|
||||
def sell_for_cash(self, item, amount = 1):
|
||||
cost = self.db.execute_and_fetch(
|
||||
"SELECT public.sell_item_for_cash(%s, %s, %s, %s, %s)",
|
||||
[item.id, amount, user.id, self.currency.id, f"BrmBar sale of {amount}x {item.name} for cash"]
|
||||
)[0]#[0]
|
||||
# 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
|
||||
## 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):
|
||||
# Undo sale; rarely needed
|
||||
#(buy, sell) = item.currency.rates(self.currency)
|
||||
#cost = amount * sell
|
||||
#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]
|
||||
(buy, sell) = item.currency.rates(self.currency)
|
||||
cost = amount * sell
|
||||
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)
|
||||
self.db.commit()
|
||||
|
||||
return cost
|
||||
|
||||
def add_credit(self, credit, user):
|
||||
self.db.execute_and_fetch(
|
||||
"SELECT public.add_credit(%s, %s, %s, %s)",
|
||||
[self.cash.id, credit, user.id, user.name]
|
||||
)
|
||||
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()
|
||||
|
||||
#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):
|
||||
self.db.execute_and_fetch(
|
||||
"SELECT public.withdraw_credit(%s, %s, %s, %s)",
|
||||
[self.cash.id, credit, user.id, user.name]
|
||||
)
|
||||
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()
|
||||
#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):
|
||||
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, sell) = item.currency.rates(self.currency)
|
||||
#cost = amount * buy
|
||||
(buy, sell) = item.currency.rates(self.currency)
|
||||
cost = amount * buy
|
||||
|
||||
#transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name))
|
||||
#item.debit(transaction, amount, "Cash")
|
||||
#self.cash.credit(transaction, cost, item.name)
|
||||
transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name))
|
||||
item.debit(transaction, amount, "Cash")
|
||||
self.cash.credit(transaction, cost, item.name)
|
||||
self.db.commit()
|
||||
|
||||
return cost
|
||||
|
||||
def receipt_to_credit(self, user, credit, description):
|
||||
#transaction = self._transaction(responsible = user, description = "Receipt: " + description)
|
||||
#self.profits.credit(transaction, credit, user.name)
|
||||
#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]
|
||||
transaction = self._transaction(responsible = user, description = "Receipt: " + description)
|
||||
self.profits.credit(transaction, credit, user.name)
|
||||
user.credit(transaction, credit, "Credit from receipt: " + description)
|
||||
self.db.commit()
|
||||
|
||||
def _transaction(self, responsible = None, description = None):
|
||||
|
@ -150,25 +102,18 @@ class Shop:
|
|||
transaction = transaction[0]
|
||||
return transaction
|
||||
|
||||
def credit_balance(self, overflow=None):
|
||||
def credit_balance(self):
|
||||
# We assume all debt accounts share a currency
|
||||
sumselect = """
|
||||
SELECT SUM(ts.amount)
|
||||
FROM accounts AS a
|
||||
LEFT JOIN transaction_splits AS ts ON a.id = ts.account
|
||||
WHERE a.acctype = %s AND ts.side = %s
|
||||
SELECT SUM(crbalance)
|
||||
FROM accounts WHERE acctype = %s
|
||||
"""
|
||||
if overflow is not None:
|
||||
sumselect += ' AND a.name ' + ('NOT ' if overflow == 'exclude' else '') + ' LIKE \'%%-overflow\''
|
||||
cur = self.db.execute_and_fetch(sumselect, ["debt", 'debit'])
|
||||
debit = cur[0] or 0
|
||||
credit = self.db.execute_and_fetch(sumselect, ["debt", 'credit'])
|
||||
credit = credit[0] or 0
|
||||
return debit - credit
|
||||
def credit_negbalance_str(self, overflow=None):
|
||||
return self.currency.str(-self.credit_balance(overflow=overflow))
|
||||
cur = self.db.execute_and_fetch(sumselect, ["debt"])
|
||||
cur = cur[0] or 0
|
||||
return cur
|
||||
def credit_negbalance_str(self):
|
||||
return self.currency.str(-self.credit_balance())
|
||||
|
||||
# XXX causing extra heavy delay ( thousands of extra SQL queries ), disabled
|
||||
def inventory_balance(self):
|
||||
balance = 0
|
||||
# Each inventory account has its own currency,
|
||||
|
@ -181,116 +126,75 @@ class Shop:
|
|||
# might have been bought for a different price! Therefore,
|
||||
# we need to replace the command below with a complex SQL
|
||||
# statement that will... ugh, accounting is hard!
|
||||
b = inv.balance() * inv.currency.rates(self.currency)[0]
|
||||
# if b != 0:
|
||||
# print(str(b) + ',' + inv.name)
|
||||
balance += b
|
||||
balance += inv.balance() * inv.currency.rates(self.currency)[0]
|
||||
return balance
|
||||
|
||||
# XXX bypass hack
|
||||
def inventory_balance_str(self):
|
||||
# return self.currency.str(self.inventory_balance())
|
||||
return "XXX"
|
||||
return self.currency.str(self.inventory_balance())
|
||||
|
||||
def account_list(self, acctype, like_str="%%"):
|
||||
"""list all accounts (people or items, as per acctype)"""
|
||||
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])
|
||||
#FIXME: sanitize input like_str ^
|
||||
#FIXME: sanitize input like_str ^
|
||||
for inventory in cur:
|
||||
accts += [ Account.load(self.db, id = inventory[0]) ]
|
||||
return accts
|
||||
|
||||
def fix_inventory(self, item, amount):
|
||||
rv = self.db.execute_and_fetch(
|
||||
"SELECT public.fix_inventory(%s, %s, %s, %s, %s, %s)",
|
||||
[item.id, item.currency.id, self.excess.id, self.deficit.id, self.currency.id, amount]
|
||||
)[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
|
||||
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
|
||||
def fix_cash(self, amount):
|
||||
rv = self.db.execute_and_fetch(
|
||||
"SELECT public.fix_cash(%s, %s, %s, %s)",
|
||||
[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
|
||||
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
|
||||
def consolidate(self):
|
||||
msg = self.db.execute_and_fetch(
|
||||
"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()
|
||||
transaction = self._transaction(description = "BrmBar inventory consolidation")
|
||||
|
||||
def undo(self, oldtid):
|
||||
#description = self.db.execute_and_fetch("SELECT description FROM transactions WHERE id = %s", [oldtid])[0]
|
||||
#description = 'undo %d (%s)' % (oldtid, description)
|
||||
|
||||
#transaction = self._transaction(description=description)
|
||||
#for split in self.db.execute_and_fetchall("SELECT id, side, account, amount, memo FROM transaction_splits WHERE transaction = %s", [oldtid]):
|
||||
# splitid, side, account, amount, memo = split
|
||||
# memo = 'undo %d (%s)' % (splitid, memo)
|
||||
# amount = -amount
|
||||
# self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, account, amount, memo])
|
||||
transaction = self.db.execute_and_fetch("SELECT public.undo_transaction(%s)",[oldtid])[0]
|
||||
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.")
|
||||
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