diff --git a/.gitignore b/.gitignore index 9eccd17..3268211 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ .*.sw? -*~ diff --git a/barcode-generator/.gitignore b/barcode-generator/.gitignore deleted file mode 100644 index e6936f1..0000000 --- a/barcode-generator/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -barcodes*.svg -barcode-generator.txt diff --git a/barcode-generator/barcode-generator.py b/barcode-generator/barcode-generator.py deleted file mode 100755 index f2d30a3..0000000 --- a/barcode-generator/barcode-generator.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python -# -# requires zint binary from zint package -# - -from subprocess import Popen, PIPE -import sys - -svghead = """ - - -""" - -svgfoot = """ -""" - -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, ' %s' % 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() diff --git a/barcode-generator/howto.txt b/barcode-generator/howto.txt deleted file mode 100644 index 4dfba63..0000000 --- a/barcode-generator/howto.txt +++ /dev/null @@ -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 - diff --git a/brmbar3/.gitignore b/brmbar3/.gitignore index 4a5b2ab..bee8a64 100644 --- a/brmbar3/.gitignore +++ b/brmbar3/.gitignore @@ -1,3 +1 @@ __pycache__ -*.log -brmbar/*.pyc diff --git a/brmbar3/PURGE.txt b/brmbar3/PURGE.txt deleted file mode 100644 index c5e5b30..0000000 --- a/brmbar3/PURGE.txt +++ /dev/null @@ -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. diff --git a/brmbar3/SQL b/brmbar3/SQL index 04667f4..6fec1d2 100644 --- a/brmbar3/SQL +++ b/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; diff --git a/brmbar3/SQL-for-RO-access.sql b/brmbar3/SQL-for-RO-access.sql deleted file mode 100644 index b8c7291..0000000 --- a/brmbar3/SQL-for-RO-access.sql +++ /dev/null @@ -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; -$$; - - diff --git a/brmbar3/SQL-schema-v001.sql b/brmbar3/SQL-schema-v001.sql deleted file mode 100644 index 44b9693..0000000 --- a/brmbar3/SQL-schema-v001.sql +++ /dev/null @@ -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 : - diff --git a/brmbar3/SQL-schema-v002.sql b/brmbar3/SQL-schema-v002.sql deleted file mode 100644 index bfb63fa..0000000 --- a/brmbar3/SQL-schema-v002.sql +++ /dev/null @@ -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 : diff --git a/brmbar3/USAGE.md b/brmbar3/USAGE.md index d749a3a..5821232 100644 --- a/brmbar3/USAGE.md +++ b/brmbar3/USAGE.md @@ -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; diff --git a/brmbar3/USEFUL.txt b/brmbar3/USEFUL.txt deleted file mode 100644 index 11c0071..0000000 --- a/brmbar3/USEFUL.txt +++ /dev/null @@ -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 - diff --git a/brmbar3/alert.sh b/brmbar3/alert.sh deleted file mode 100755 index 05496e6..0000000 --- a/brmbar3/alert.sh +++ /dev/null @@ -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 diff --git a/brmbar3/autostock.py b/brmbar3/autostock.py deleted file mode 100755 index 23d1e8d..0000000 --- a/brmbar3/autostock.py +++ /dev/null @@ -1,45 +0,0 @@ -#! /usr/bin/env python3 - -import argparse -import brmbar -import math -from brmbar import Database -import sys - -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__": - print("!!! THIS PROGRAM NO LONGER WORKS !!!") - sys.exit(1) - main() - diff --git a/brmbar3/brmbar-cli.py b/brmbar3/brmbar-cli.py index a0545ab..d91a24b 100755 --- a/brmbar3/brmbar-cli.py +++ b/brmbar3/brmbar-cli.py @@ -6,8 +6,6 @@ from brmbar import Database import brmbar -print("!!! THIS PROGRAM NO LONGER WORKS !!!") -sys.exit(1) def help(): print("""BrmBar v3 (c) Petr Baudis 2012-2013 @@ -17,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 @@ -34,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 @@ -58,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) @@ -99,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) @@ -128,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": @@ -151,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())) @@ -178,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.") @@ -216,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: @@ -246,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() diff --git a/brmbar3/brmbar-gui-qt4.py b/brmbar3/brmbar-gui-qt4.py index 4eb668c..23e6d37 100755 --- a/brmbar3/brmbar-gui-qt4.py +++ b/brmbar3/brmbar-gui-qt4.py @@ -1,7 +1,6 @@ #!/usr/bin/python3 import sys -import subprocess from PySide import QtCore, QtGui, QtDeclarative @@ -9,29 +8,6 @@ from brmbar import Database import brmbar -import argparse - -import logging - -root = logging.getLogger() -root.setLevel(logging.DEBUG) - -handler = logging.StreamHandler(sys.stdout) -handler.setLevel(logging.DEBUG) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -handler.setFormatter(formatter) -root.addHandler(handler) -logger = logging.getLogger(__name__) - -# 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 """ @@ -53,23 +29,13 @@ 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 def acct_map(self, acct): if acct is None: - logger.debug("acct_map: acct is None") return None - logger.debug("acct_map: acct.acctype=%s", acct.acctype) if acct.acctype == 'debt': return self.acct_debt_map(acct) elif acct.acctype == "inventory": @@ -88,14 +54,12 @@ class ShopAdapter(QtCore.QObject): Therefore, we construct a map that we can pass around easily. We return None on unrecognized barcode. """ barcode = str(barcode) - logger.debug("barcodeInput: barcode='%s'", barcode) if barcode and barcode[0] == "$": credits = {'$02': 20, '$05': 50, '$10': 100, '$20': 200, '$50': 500, '$1k': 1000} credit = credits[barcode] if credit is None: return None return { "acctype": "recharge", "amount": str(credit)+".00" } - logger.debug("barcodeInput: before load_by_barcode") acct = self.acct_map(brmbar.Account.load_by_barcode(db, barcode)) db.commit() return acct @@ -106,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) @@ -133,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() @@ -148,45 +99,23 @@ class ShopAdapter(QtCore.QObject): db.commit() return balance - @QtCore.Slot('QVariant', 'QVariant', 'QVariant', result='QVariant') - def newTransfer(self, uidfrom, uidto, amount): - logger.debug("newTransfer %s %s %s", uidfrom, uidto, amount) - ufrom = brmbar.Account.load(db, id=uidfrom) - logger.debug(" ufrom = %s", ufrom) - uto = brmbar.Account.load(db, id=uidto) - logger.debug(" uto = %s", uto) - shop.transfer_credit(ufrom, uto, amount = amount) - db.commit() - csfa = currency.str(float(amount)) - logger.debug(" csfa = '%s'", csfa) - return csfa - - @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): - return "N/A" balance = shop.cash.balance_str() db.commit() return balance @QtCore.Slot(result='QVariant') def balance_profit(self): - return "N/A" balance = shop.profits.balance_str() db.commit() return balance @QtCore.Slot(result='QVariant') def balance_inventory(self): - return "N/A" balance = shop.inventory_balance_str() db.commit() return balance @QtCore.Slot(result='QVariant') def balance_credit(self): - return "N/A" balance = shop.credit_negbalance_str() db.commit() return balance @@ -199,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 @@ -251,23 +180,7 @@ class ShopAdapter(QtCore.QObject): db.commit() return balance -parser = argparse.ArgumentParser() -parser.add_argument("--dbname", help="Database name", type=str) -parser.add_argument("--dbuser", help="Database user", type=str) -parser.add_argument("--dbhost", help="Database host", type=str) -parser.add_argument("--dbpass", help="Database user password", type=str) -args = parser.parse_args() -argdbname = args.dbname -argdbuser = args.dbuser -argdbhost = args.dbhost -argdbpass = args.dbpass - -db = Database.Database( - "dbname={0} user={1} host={2} password={3}".format( - argdbname,argdbuser,argdbhost,argdbpass - ) -) - +db = Database.Database("dbname=brmbar") shop = brmbar.Shop.new_with_defaults(db) currency = shop.currency db.commit() diff --git a/brmbar3/brmbar-gui-qt4/ChargeCredit.qml b/brmbar3/brmbar-gui-qt4/ChargeCredit.qml index cdcc006..8eda557 100644 --- a/brmbar3/brmbar-gui-qt4/ChargeCredit.qml +++ b/brmbar3/brmbar-gui-qt4/ChargeCredit.qml @@ -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") - } } diff --git a/brmbar3/brmbar-gui-qt4/ItemInfo.qml b/brmbar3/brmbar-gui-qt4/ItemInfo.qml index 11e959d..a39e322 100644 --- a/brmbar3/brmbar-gui-qt4/ItemInfo.qml +++ b/brmbar3/brmbar-gui-qt4/ItemInfo.qml @@ -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") diff --git a/brmbar3/brmbar-gui-qt4/MainPage.qml b/brmbar3/brmbar-gui-qt4/MainPage.qml index d11fd0f..8155943 100644 --- a/brmbar3/brmbar-gui-qt4/MainPage.qml +++ b/brmbar3/brmbar-gui-qt4/MainPage.qml @@ -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. *" - } } diff --git a/brmbar3/brmbar-gui-qt4/Transfer.qml b/brmbar3/brmbar-gui-qt4/Transfer.qml deleted file mode 100644 index d6a3a4e..0000000 --- a/brmbar3/brmbar-gui-qt4/Transfer.qml +++ /dev/null @@ -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") - } - } -} diff --git a/brmbar3/brmbar-gui-qt4/Withdraw.qml b/brmbar3/brmbar-gui-qt4/Withdraw.qml index 2d8c49b..a9e7700 100644 --- a/brmbar3/brmbar-gui-qt4/Withdraw.qml +++ b/brmbar3/brmbar-gui-qt4/Withdraw.qml @@ -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") } } diff --git a/brmbar3/brmbar-tui.py b/brmbar3/brmbar-tui.py index a2dc10b..00bdf44 100755 --- a/brmbar3/brmbar-tui.py +++ b/brmbar3/brmbar-tui.py @@ -6,9 +6,6 @@ from brmbar import Database import brmbar -print("!!! THIS PROGRAM NO LONGER WORKS !!!") -sys.exit(1) - db = Database.Database("dbname=brmbar") shop = brmbar.Shop.new_with_defaults(db) currency = shop.currency diff --git a/brmbar3/brmbar-web.py b/brmbar3/brmbar-web.py index 522e9ed..0ec70dc 100755 --- a/brmbar3/brmbar-web.py +++ b/brmbar3/brmbar-web.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import sys @@ -6,9 +6,6 @@ from brmbar import Database import brmbar -print("!!! THIS PROGRAM NO LONGER WORKS !!!") -sys.exit(1) - from flask import * app = Flask(__name__) #app.debug = True diff --git a/brmbar3/brmbar/Account.py b/brmbar3/brmbar/Account.py index 2499d05..0c6448f 100644 --- a/brmbar3/brmbar/Account.py +++ b/brmbar3/brmbar/Account.py @@ -1,15 +1,10 @@ from .Currency import Currency -import logging - -logger = logging.getLogger(__name__) - class Account: - """BrmBar Account + """ BrmBar Account Both users and items are accounts. So is the money box, etc. Each account has a currency.""" - def __init__(self, db, id, name, currency, acctype): self.db = db self.id = id @@ -19,35 +14,40 @@ class Account: @classmethod def load_by_barcode(cls, db, barcode): - logger.debug("load_by_barcode: '%s'", barcode) - account_id, account_name, account_acctype, currency_id, currency_name = db.execute_and_fetch( - "SELECT account_id, account_name, account_acctype, currency_id, currency_name FROM public.account_class_initialization_data('by_barcode', NULL, %s)", - [barcode]) - currency = Currency(db, currency_id, currency_name) - return cls(db, account_id, account_name, currency, account_acctype) + res = db.execute_and_fetch("SELECT account FROM barcodes WHERE barcode = %s", [barcode]) + if res is None: + return None + id = res[0] + return cls.load(db, id = id) @classmethod - def load(cls, db, id=None): - """Constructor for existing account""" - account_id, account_name, account_acctype, currency_id, currency_name = db.execute_and_fetch( - "SELECT account_id, account_name, account_acctype, currency_id, currency_name FROM public.account_class_initialization_data('by_id', %s, NULL)", - [id]) - currency = Currency(db, currency_id, currency_name) - return cls(db, account_id, account_name, currency, account_acctype) + def load(cls, db, id = None, name = None): + """ Constructor for existing account """ + if id is not None: + name = db.execute_and_fetch("SELECT name FROM accounts WHERE id = %s", [id]) + name = name[0] + elif name is not None: + id = db.execute_and_fetch("SELECT id FROM accounts WHERE name = %s", [name]) + id = id[0] + else: + raise NameError("Account.load(): Specify either id or name") + + currid, acctype = db.execute_and_fetch("SELECT currency, acctype FROM accounts WHERE id = %s", [id]) + currency = Currency.load(db, id = currid) + + return cls(db, name = name, id = id, currency = currency, acctype = acctype) @classmethod def create(cls, db, name, currency, acctype): - """Constructor for new account""" - id = db.execute_and_fetch( - "SELECT public.create_account(%s, %s, %s)", [name, currency.id, acctype] - ) - return cls(db, name=name, id=id, currency=currency, acctype=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 = 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 + 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()) @@ -55,12 +55,23 @@ class Account: def negbalance_str(self): return self.currency.str(-self.balance()) + def debit(self, transaction, amount, memo): + return self._transaction_split(transaction, 'debit', amount, memo) + + 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( - "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("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 diff --git a/brmbar3/brmbar/Currency.py b/brmbar3/brmbar/Currency.py index fba3059..d2216ca 100644 --- a/brmbar3/brmbar/Currency.py +++ b/brmbar3/brmbar/Currency.py @@ -1,12 +1,10 @@ # vim: set fileencoding=utf8 - class Currency: - """Currency - + """ Currency + Each account has a currency (1 Kč, 1 Club Maté, ...), pairs of - currencies have (asymmetric) exchange rates.""" - + currencies have (asymmetric) exchange rates. """ def __init__(self, db, id, name): self.db = db self.id = id @@ -14,70 +12,63 @@ class Currency: @classmethod def default(cls, db): - """Default wallet currency""" - return cls.load(db, name="Kč") + """ Default wallet currency """ + return cls.load(db, name = "Kč") @classmethod - def load(cls, db, id=None): - """Constructor for existing currency""" - if id is None: - raise NameError("Currency.load(): Specify id") - name = db.execute_and_fetch("SELECT name FROM currencies WHERE id = %s", [id]) - name = name[0] - return cls(db, id=id, name=name) + def load(cls, db, id = None, name = None): + """ Constructor for existing currency """ + if id is not None: + name = db.execute_and_fetch("SELECT name FROM currencies WHERE id = %s", [id]) + name = name[0] + elif name is not None: + id = db.execute_and_fetch("SELECT id FROM currencies WHERE name = %s", [name]) + id = id[0] + else: + raise NameError("Currency.load(): Specify either id or name") + return cls(db, name = name, id = id) @classmethod def create(cls, db, name): - """Constructor for new currency""" - id = db.execute_and_fetch("SELECT public.create_currency(%s)", [name]) - return cls(db, id=id, name=name) + """ Constructor for new currency """ + 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): + """ 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] - ) + $sell is the price of $self in means of $other when selling it (from brmbar) """ + + 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("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] - ) + raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name()) + buy_rate, buy_rate_dir = res + buy = buy_rate if buy_rate_dir == "target_to_source" else 1/buy_rate + + 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", [other.id, self.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() - ) + raise NameError("Currency.rate(): Unknown conversion " + self.name() + " to " + other.name()) + sell_rate, sell_rate_dir = res + sell = sell_rate if sell_rate_dir == "source_to_target" else 1/sell_rate 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: + raise NameError("Currency.convert(): Unknown conversion " + self.name() + " to " + target.name()) + rate, rate_dir = res + if rate_dir == "source_to_target": + resamount = amount * rate + else: + resamount = amount / rate + return resamount + def str(self, amount): return "{:.2f} {}".format(amount, self.name) def update_sell_rate(self, target, rate): - 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( - "SELECT public.update_currency_buy_rate(%s, %s, %s)", - [self.id, source.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"]) diff --git a/brmbar3/brmbar/Database.py b/brmbar3/brmbar/Database.py index 5b844dd..d0202d4 100644 --- a/brmbar3/brmbar/Database.py +++ b/brmbar3/brmbar/Database.py @@ -3,30 +3,25 @@ import psycopg2 from contextlib import closing import time -import logging - -logger = logging.getLogger(__name__) - class Database: """self-reconnecting database object""" - def __init__(self, dsn): self.db_conn = psycopg2.connect(dsn) self.dsn = dsn - def execute(self, query, attrs=None): + def execute(self, query, attrs = None): """execute a query and return one result""" with closing(self.db_conn.cursor()) as cur: cur = self._execute(cur, query, attrs) - def execute_and_fetch(self, query, attrs=None): + def execute_and_fetch(self, query, attrs = None): """execute a query and return one result""" with closing(self.db_conn.cursor()) as cur: cur = self._execute(cur, query, attrs) return cur.fetchone() - def execute_and_fetchall(self, query, attrs=None): + def execute_and_fetchall(self, query, attrs = None): """execute a query and return all results""" with closing(self.db_conn.cursor()) as cur: cur = self._execute(cur, query, attrs) @@ -35,39 +30,31 @@ class Database: def _execute(self, cur, query, attrs, level=1): """execute a query, and in case of OperationalError (db restart) reconnect to database. Recurses with increasig pause between tries""" - logger.debug("SQL: (%s) @%s" % (query, time.strftime("%Y%m%d %a %I:%m %p"))) try: if attrs is None: cur.execute(query) else: cur.execute(query, attrs) return cur - except ( - psycopg2.DataError - ) as error: # when biitr comes and enters '99999999999999999999' for amount - logger.debug( - "We have invalid input data (SQLi?): level %s (%s) @%s" - % (level, error, time.strftime("%Y%m%d %a %I:%m %p")) - ) + except psycopg2.DataError as error: # when biitr comes and enters '99999999999999999999' for amount + print("We have invalid input data (SQLi?): level %s (%s) @%s" % ( + level, error, time.strftime("%Y%m%d %a %I:%m %p") + )) self.db_conn.rollback() raise RuntimeError("Unsanitized data entered again... BOBBY TABLES") except psycopg2.OperationalError as error: - logger.debug( - "Sleeping: level %s (%s) @%s" - % (level, error, time.strftime("%Y%m%d %a %I:%m %p")) - ) - # TODO: emit message "db conn failed, reconnecting - time.sleep(2**level) + print("Sleeping: level %s (%s) @%s" % ( + level, error, time.strftime("%Y%m%d %a %I:%m %p") + )) + #TODO: emit message "db conn failed, reconnecting + time.sleep(2 ** level) try: self.db_conn = psycopg2.connect(self.dsn) except psycopg2.OperationalError: - # TODO: emit message "psql not running to interface + #TODO: emit message "psql not running to interface time.sleep(1) - cur = self.db_conn.cursor() # how ugly is this? - return self._execute(cur, query, attrs, level + 1) - except Exception as ex: - logger.debug("_execute exception: %s", ex) - self.db_conn.rollback() + cur = self.db_conn.cursor() #how ugly is this? + return self._execute(cur, query, attrs, level+1) def commit(self): """passes commit to db""" diff --git a/brmbar3/brmbar/Shop.py b/brmbar3/brmbar/Shop.py index a2ef1d1..b5c61dc 100644 --- a/brmbar3/brmbar/Shop.py +++ b/brmbar3/brmbar/Shop.py @@ -1,249 +1,200 @@ import brmbar from .Currency import Currency from .Account import Account -import logging - -logger = logging.getLogger(__name__) - class Shop: - """BrmBar Shop + """ BrmBar Shop Business logic so that only interaction is left in the hands - of the frontend scripts.""" - - def __init__(self, db, currency_id, profits_id, cash_id, excess_id, deficit_id): - # Keep db as-is + of the frontend scripts. """ + def __init__(self, db, currency, profits, cash, excess, deficit): self.db = db - - # Store all ids - self.currency_id = currency_id - self.profits_id = profits_id - self.cash_id = cash_id - self.excess_id = excess_id - self.deficit_id = deficit_id - - # Create objects where needed for legacy code - - # brmbar.Currency - self.currency = Currency.load(self.db, id=self.currency_id) - - # income brmbar.Account for brmbar profit margins on items - self.profits = Account.load(db, id=self.profits_id) - - # our operational ("wallet") cash account - self.cash = Account.load(db, id=self.cash_id) - - # account from which is deducted cash during inventory item - # fixing (when system contains less items than is the - # reality) - self.excess = Account.load(db, id=self.excess_id) - - # account where is put cash during inventory item fixing (when - # system contains more items than is the reality) - self.deficit = Account.load(db, id=self.deficit_id) + self.currency = currency # brmbar.Currency + self.profits = profits # income brmbar.Account for brmbar profit margins on items + self.cash = cash # our operational ("wallet") cash account + self.excess = excess # account from which is deducted cash during inventory item fixing (when system contains less items than is the reality) + self.deficit = deficit # account where is put cash during inventory item fixing (when system contains more items than is the reality) @classmethod def new_with_defaults(cls, db): - # shop_class_initialization_data - currency_id, profits_id, cash_id, excess_id, deficit_id = db.execute_and_fetch( - "select currency_id, profits_id, cash_id, excess_id, deficit_id from public.shop_class_initialization_data()" - ) - return cls( - db, - currency_id=currency_id, - profits_id=profits_id, - cash_id=cash_id, - excess_id=excess_id, - deficit_id=deficit_id, - ) + return cls(db, + currency = Currency.default(db), + profits = Account.load(db, name = "BrmBar Profits"), + cash = Account.load(db, name = "BrmBar Cash"), + excess = Account.load(db, name = "BrmBar Excess"), + deficit = Account.load(db, name = "BrmBar Deficit")) - def sell(self, item, user, amount=1): - # Call the stored procedure for the sale - logger.debug( - "sell: item.id=%s amount=%s user.id=%s self.currency.id=%s", - item.id, - amount, - user.id, - self.currency.id, - ) - res = self.db.execute_and_fetch( - "SELECT public.sell_item(%s, %s, %s, %s, %s)", - [ - item.id, - amount, - user.id, - self.currency.id, - "BrmBar sale of {0}x {1} to {2}".format(amount, item.name, user.name), - ], - ) - logger.debug("sell: res[0]=%s", res[0]) - cost = res[0] + def sell(self, item, user, amount = 1): + # 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(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, - "BrmBar sale of {0}x {1} for cash".format(amount, item.name), - ], - )[0] + def sell_for_cash(self, item, amount = 1): + # 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): - # 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, - "BrmBar sale UNDO of {0}x {1} to {2}".format( - amount, item.name, user.name - ), - ], - )[0] + 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) 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() 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() - def transfer_credit(self, userfrom, userto, amount): - self.db.execute_and_fetch( - "SELECT public.transfer_credit(%s, %s, %s, %s, %s, %s)", - [self.cash.id, amount, userfrom.id, userfrom.name, userto.id, userto.name], - ) - self.db.commit() + def buy_for_cash(self, item, amount = 1): + # Buy: Currency conversion from item currency to shop currency + (buy, sell) = item.currency.rates(self.currency) + cost = amount * buy - def buy_for_cash(self, item, amount=1): - iamount = int(amount) - famount = float(iamount) - assert famount == amount, "amount is not integer value %s".format(amount) - cost = self.db.execute_and_fetch( - "SELECT public.buy_for_cash(%s, %s, %s, %s, %s)", - [self.cash.id, item.id, iamount, self.currency.id, item.name], - )[0] + 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): - self.db.execute_and_fetch( - "SELECT public.receipt_reimbursement(%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): - transaction = self.db.execute_and_fetch( - "INSERT INTO transactions (responsible, description) VALUES (%s, %s) RETURNING id", - [responsible.id if responsible else None, description], - ) + def _transaction(self, responsible = None, description = None): + transaction = self.db.execute_and_fetch("INSERT INTO transactions (responsible, description) VALUES (%s, %s) RETURNING id", + [responsible.id if responsible else None, description]) 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()) def inventory_balance(self): - resa = self.db.execute_and_fetch("SELECT * FROM public.inventory_balance()") - res = resa[0] - logger.debug("inventory_balance resa = %s", resa) - return res - + balance = 0 + # Each inventory account has its own currency, + # so we just do this ugly iteration + cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s", ["inventory"]) + for inventory in cur: + invid = inventory[0] + inv = Account.load(self.db, id = invid) + # FIXME: This is not correct as each instance of inventory + # 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! + balance += inv.balance() * inv.currency.rates(self.currency)[0] + return balance def inventory_balance_str(self): 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 a.id, a.name aname, a.currency, a.acctype, c.name cname FROM accounts a JOIN currencies c ON c.id=a.currency WHERE a.acctype = %s AND a.name ILIKE %s ORDER BY a.name ASC", - [acctype, like_str], - ) - # FIXME: sanitize input like_str ^ + cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s AND name ILIKE %s ORDER BY name ASC", [acctype, like_str]) + #FIXME: sanitize input like_str ^ for inventory in cur: - curr = Currency(db=self.db, id=inventory[2], name=inventory[4]); - accts += [Account(self.db, id=inventory[0], name=inventory[1], currency=curr, acctype=inventory[3])] + 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 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 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] - if msg != None: - print(msg) - self.db.commit() + transaction = self._transaction(description = "BrmBar inventory consolidation") - def undo(self, oldtid): - 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 diff --git a/brmbar3/crontab b/brmbar3/crontab deleted file mode 100644 index 2e66748..0000000 --- a/brmbar3/crontab +++ /dev/null @@ -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 - diff --git a/brmbar3/dluhy.sh b/brmbar3/dluhy.sh deleted file mode 100755 index 61c2a46..0000000 --- a/brmbar3/dluhy.sh +++ /dev/null @@ -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 diff --git a/brmbar3/log.sh b/brmbar3/log.sh deleted file mode 100755 index b1afbdb..0000000 --- a/brmbar3/log.sh +++ /dev/null @@ -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 diff --git a/brmbar3/schema/0001-init.sql b/brmbar3/schema/0001-init.sql deleted file mode 100644 index 8e7f8db..0000000 --- a/brmbar3/schema/0001-init.sql +++ /dev/null @@ -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 --- --- 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; -$$; diff --git a/brmbar3/schema/0002-trading-accounts.sql b/brmbar3/schema/0002-trading-accounts.sql deleted file mode 100644 index 021df3d..0000000 --- a/brmbar3/schema/0002-trading-accounts.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0003-new-account.sql b/brmbar3/schema/0003-new-account.sql deleted file mode 100644 index 9ac02b5..0000000 --- a/brmbar3/schema/0003-new-account.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0004-add-account-barcode.sql b/brmbar3/schema/0004-add-account-barcode.sql deleted file mode 100644 index dbdba9a..0000000 --- a/brmbar3/schema/0004-add-account-barcode.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0005-rename-account.sql b/brmbar3/schema/0005-rename-account.sql deleted file mode 100644 index a957029..0000000 --- a/brmbar3/schema/0005-rename-account.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0006-new-currency.sql b/brmbar3/schema/0006-new-currency.sql deleted file mode 100644 index 0ed9a94..0000000 --- a/brmbar3/schema/0006-new-currency.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0007-update-currency-sell-rate.sql b/brmbar3/schema/0007-update-currency-sell-rate.sql deleted file mode 100644 index f627897..0000000 --- a/brmbar3/schema/0007-update-currency-sell-rate.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0008-update-currency-buy-rate.sql b/brmbar3/schema/0008-update-currency-buy-rate.sql deleted file mode 100644 index cbab11b..0000000 --- a/brmbar3/schema/0008-update-currency-buy-rate.sql +++ /dev/null @@ -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 --- --- 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$; diff --git a/brmbar3/schema/0009-shop-sell.sql b/brmbar3/schema/0009-shop-sell.sql deleted file mode 100644 index 6468bdb..0000000 --- a/brmbar3/schema/0009-shop-sell.sql +++ /dev/null @@ -1,149 +0,0 @@ --- --- 0009-shop-sell.sql --- --- #9 - stored function for sell transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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, rate_dir INTO STRICT v_rate, 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, rate_dir INTO STRICT v_rate, 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.id%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 : diff --git a/brmbar3/schema/0010-shop-sell-for-cash.sql b/brmbar3/schema/0010-shop-sell-for-cash.sql deleted file mode 100644 index 86fcf9c..0000000 --- a/brmbar3/schema/0010-shop-sell-for-cash.sql +++ /dev/null @@ -1,154 +0,0 @@ --- --- 0010-shop-sell-for-cash.sql --- --- #10 - stored function for cash sell transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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.id%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; - v_item_currency_id public.accounts.currency%TYPE; -BEGIN - -- Get item's currency - SELECT currency - INTO v_item_currency_id - FROM public.accounts - WHERE id=i_item_id; - - -- Get the buy and sell rates from the stored functions - v_buy_rate := public.find_buy_rate(v_item_currency_id, i_target_currency_id); - v_sell_rate := public.find_sell_rate(v_item_currency_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 (v_transaction_id, 'credit', i_item_id, i_amount, - i_other_memo); - - -- the user - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (v_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 (v_transaction_id, - 'debit', - (SELECT id FROM public.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_user_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_user_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 : diff --git a/brmbar3/schema/0011-shop-undo-sale.sql b/brmbar3/schema/0011-shop-undo-sale.sql deleted file mode 100644 index 4427070..0000000 --- a/brmbar3/schema/0011-shop-undo-sale.sql +++ /dev/null @@ -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 --- --- 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.id%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 : diff --git a/brmbar3/schema/0012-shop-add-credit.sql b/brmbar3/schema/0012-shop-add-credit.sql deleted file mode 100644 index 00318ae..0000000 --- a/brmbar3/schema/0012-shop-add-credit.sql +++ /dev/null @@ -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 --- --- 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 : diff --git a/brmbar3/schema/0013-shop-withdraw-credit.sql b/brmbar3/schema/0013-shop-withdraw-credit.sql deleted file mode 100644 index d379186..0000000 --- a/brmbar3/schema/0013-shop-withdraw-credit.sql +++ /dev/null @@ -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 --- --- 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 : diff --git a/brmbar3/schema/0014-shop-transfer-credit.sql b/brmbar3/schema/0014-shop-transfer-credit.sql deleted file mode 100644 index 7e13c03..0000000 --- a/brmbar3/schema/0014-shop-transfer-credit.sql +++ /dev/null @@ -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 --- --- 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 : diff --git a/brmbar3/schema/0015-shop-buy-for-cash.sql b/brmbar3/schema/0015-shop-buy-for-cash.sql deleted file mode 100644 index aa66260..0000000 --- a/brmbar3/schema/0015-shop-buy-for-cash.sql +++ /dev/null @@ -1,85 +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 --- --- 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; - v_item_currency_id public.accounts.currency%TYPE; -BEGIN - -- Get item's currency - SELECT currency - INTO STRICT v_item_currency_id - FROM public.accounts - WHERE id=i_item_id; - -- Get the buy rates from the stored functions - v_buy_rate := public.find_buy_rate(v_item_currency_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 (v_transaction_id, 'debit', i_item_id, i_amount, - 'Cash'); - - -- the cash - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (v_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 : diff --git a/brmbar3/schema/0016-shop-receipt-to-credit.sql b/brmbar3/schema/0016-shop-receipt-to-credit.sql deleted file mode 100644 index db77f7a..0000000 --- a/brmbar3/schema/0016-shop-receipt-to-credit.sql +++ /dev/null @@ -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 --- --- 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 (v_transaction_id, 'credit', i_profits_id, i_amount, i_user_name); - -- the user - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (v_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 : diff --git a/brmbar3/schema/0017-shop-fix-inventory.sql b/brmbar3/schema/0017-shop-fix-inventory.sql deleted file mode 100644 index a011892..0000000 --- a/brmbar3/schema/0017-shop-fix-inventory.sql +++ /dev/null @@ -1,159 +0,0 @@ --- --- 0017-shop-fix-inventory.sql --- --- #17 - stored function for "fixing" inventory transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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, - COALESCE(SUM(CASE WHEN side='debit' THEN amount ELSE 0 END),0) dbsum - INTO v_crsum, v_dbsum - FROM public.transaction_splits ts WHERE ts.account=i_account_id; - RETURN v_dbsum - v_crsum; -END; $$; - -CREATE OR REPLACE FUNCTION brmbar_privileged.fix_account_balance( - IN i_account_id public.accounts.id%TYPE, - IN i_account_currency_id public.currencies.id%TYPE, - IN i_excess_id public.accounts.id%TYPE, - IN i_deficit_id public.accounts.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.accounts.id%TYPE, - IN i_account_currency_id public.currencies.id%TYPE, - IN i_excess_id public.accounts.id%TYPE, - IN i_deficit_id public.accounts.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 : diff --git a/brmbar3/schema/0018-shop-fix-cash.sql b/brmbar3/schema/0018-shop-fix-cash.sql deleted file mode 100644 index 5ced17d..0000000 --- a/brmbar3/schema/0018-shop-fix-cash.sql +++ /dev/null @@ -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 --- --- 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.accounts.id%TYPE, - IN i_deficit_id public.accounts.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 : diff --git a/brmbar3/schema/0019-shop-consolidate.sql b/brmbar3/schema/0019-shop-consolidate.sql deleted file mode 100644 index db06685..0000000 --- a/brmbar3/schema/0019-shop-consolidate.sql +++ /dev/null @@ -1,82 +0,0 @@ --- --- 0019-shop-consolidate.sql --- --- #19 - stored function for "consolidation" transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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 : diff --git a/brmbar3/schema/0020-shop-undo.sql b/brmbar3/schema/0020-shop-undo.sql deleted file mode 100644 index d2d12b3..0000000 --- a/brmbar3/schema/0020-shop-undo.sql +++ /dev/null @@ -1,64 +0,0 @@ --- --- 0020-shop-undo.sql --- --- #20 - stored function for undo transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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 '||i_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 : diff --git a/brmbar3/schema/0021-constraints-on-numeric-columns.sql b/brmbar3/schema/0021-constraints-on-numeric-columns.sql deleted file mode 100644 index 524f08a..0000000 --- a/brmbar3/schema/0021-constraints-on-numeric-columns.sql +++ /dev/null @@ -1,48 +0,0 @@ --- --- 0021-constraints-on-numeric-columns.sql --- --- #21 - stored function for adding constraints to numeric columns --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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(20) THEN - -ALTER TABLE public.transaction_splits ADD CONSTRAINT amount_check - CHECK (amount NOT IN ('Infinity'::numeric, '-Infinity'::numeric, 'NaN'::numeric)); - -ALTER TABLE public.exchange_rates ADD CONSTRAINT rate_check - CHECK (rate NOT IN ('Infinity'::numeric, '-Infinity'::numeric, 'NaN'::numeric)); - - -PERFORM brmbar_privileged.upgrade_schema_version_to(21); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : - diff --git a/brmbar3/schema/0022-shop-init.sql b/brmbar3/schema/0022-shop-init.sql deleted file mode 100644 index 55fe098..0000000 --- a/brmbar3/schema/0022-shop-init.sql +++ /dev/null @@ -1,85 +0,0 @@ --- --- 0022-shop-init.sql --- --- #22 - stored function for initializing Shop.py --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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$ -DECLARE - v INTEGER; -BEGIN - -IF brmbar_privileged.has_exact_schema_version(21) THEN - - -SELECT COUNT(1) INTO v - FROM pg_catalog.pg_type typ - INNER JOIN pg_catalog.pg_namespace nsp - ON nsp.oid = typ.typnamespace - WHERE nsp.nspname = 'brmbar_privileged' - AND typ.typname='shop_class_initialization_data_type'; - -IF v>0 THEN - RAISE NOTICE 'Changing type shop_class_initialization_data_type'; - DROP TYPE brmbar_privileged.shop_class_initialization_data_type CASCADE; -ELSE - RAISE NOTICE 'Creating type shop_class_initialization_data_type'; -END IF; - -CREATE TYPE brmbar_privileged.shop_class_initialization_data_type -AS ( - currency_id INTEGER, --public.currencies.id%TYPE, - profits_id INTEGER, --public.accounts.id%TYPE, - cash_id INTEGER, --public.accounts.id%TYPE, - excess_id INTEGER, --public.accounts.id%TYPE, - deficit_id INTEGER --public.accounts.id%TYPE -); - -CREATE OR REPLACE FUNCTION public.shop_class_initialization_data() -RETURNS brmbar_privileged.shop_class_initialization_data_type -LANGUAGE plpgsql -AS -$$ -DECLARE - rv brmbar_privileged.shop_class_initialization_data_type; -BEGIN - rv.currency_id := 1; - SELECT id INTO rv.profits_id FROM public.accounts WHERE name = 'BrmBar Profits'; - SELECT id INTO rv.cash_id FROM public.accounts WHERE name = 'BrmBar Cash'; - SELECT id INTO rv.excess_id FROM public.accounts WHERE name = 'BrmBar Excess'; - SELECT id INTO rv.deficit_id FROM public.accounts WHERE name = 'BrmBar Deficit'; - RETURN rv; -END; -$$; - - -PERFORM brmbar_privileged.upgrade_schema_version_to(22); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : - diff --git a/brmbar3/schema/0023-inventory-balance.sql b/brmbar3/schema/0023-inventory-balance.sql deleted file mode 100644 index 2072812..0000000 --- a/brmbar3/schema/0023-inventory-balance.sql +++ /dev/null @@ -1,94 +0,0 @@ --- --- 0023-inventory-balance.sql --- --- #23 - stored function for total inventory balance --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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(22) THEN - -CREATE OR REPLACE VIEW brmbar_privileged.debit_balances AS -SELECT - ts.account AS debit_account, - SUM(ts.amount) AS debit_sum -FROM public.transaction_splits ts -WHERE (ts.side = 'debit'::public.transaction_split_side) -GROUP BY ts.account; - -CREATE OR REPLACE VIEW brmbar_privileged.credit_balances AS -SELECT - ts.account AS credit_account, - SUM(ts.amount) AS credit_sum -FROM public.transaction_splits ts -WHERE (ts.side = 'credit'::public.transaction_split_side) -GROUP BY 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; -*/ - -CREATE OR REPLACE FUNCTION public.inventory_balance() -RETURNS DECIMAL(12,2) -VOLATILE NOT LEAKPROOF LANGUAGE plpgsql SECURITY DEFINER AS $fn$ -DECLARE - rv DECIMAL(12,2); -BEGIN - WITH inventory_balances AS ( - SELECT COALESCE(credit_sum, 0) * public.find_buy_rate(a.currency, 1) as credit_sum, - COALESCE(debit_sum, 0) * public.find_buy_rate(a.currency, 1) as debit_sum, - COALESCE(credit_account, debit_account) as cd_account - FROM brmbar_privileged.credit_balances cb - FULL OUTER JOIN brmbar_privileged.debit_balances db - ON (debit_account = credit_account) - LEFT JOIN public.accounts a - ON (a.id = COALESCE(credit_account, debit_account)) - WHERE a.acctype = 'inventory'::public.account_type - ) - SELECT SUM(debit_sum) - SUM(credit_sum) INTO rv - FROM inventory_balances; - - RETURN rv; -END; -$fn$; - - - -PERFORM brmbar_privileged.upgrade_schema_version_to(23); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/schema/0024-find-rates-fix.sql b/brmbar3/schema/0024-find-rates-fix.sql deleted file mode 100644 index 70bab06..0000000 --- a/brmbar3/schema/0024-find-rates-fix.sql +++ /dev/null @@ -1,92 +0,0 @@ --- --- 0024-find-rates-fix.sql --- --- #24 - fix stored functions find_buy_rate and find_sell_rate --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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(23) THEN - -DROP FUNCTION IF EXISTS public.find_buy_rate(integer,integer); -DROP FUNCTION IF EXISTS public.find_sell_rate(integer,integer); - -CREATE OR REPLACE FUNCTION public.find_buy_rate( - IN i_item_currency_id public.accounts.id%TYPE, - IN i_other_currency_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, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_item_currency_id AND source = i_other_currency_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; - /* propagate error -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_currency_id public.accounts.id%TYPE, - IN i_other_currency_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, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_other_currency_id AND source = i_item_currency_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; - /* propagate error -EXCEPTION - WHEN NO_DATA_FOUND THEN - RETURN -1; - */ -END; -$$; - -PERFORM brmbar_privileged.upgrade_schema_version_to(24); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/schema/0025-load-account.sql b/brmbar3/schema/0025-load-account.sql deleted file mode 100644 index 3076e0b..0000000 --- a/brmbar3/schema/0025-load-account.sql +++ /dev/null @@ -1,111 +0,0 @@ --- --- 0025-load-account.sql --- --- #25 - stored procedures for account loading --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- 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$ -DECLARE - v INTEGER; -BEGIN - -IF brmbar_privileged.has_exact_schema_version(24) THEN - -SELECT COUNT(1) INTO v - FROM pg_catalog.pg_type typ - INNER JOIN pg_catalog.pg_namespace nsp - ON nsp.oid = typ.typnamespace - WHERE nsp.nspname = 'brmbar_privileged' - AND typ.typname='account_class_initialization_data_type'; - -IF v>0 THEN - RAISE NOTICE 'Changing type account_class_initialization_data_type'; - DROP TYPE brmbar_privileged.account_class_initialization_data_type CASCADE; -ELSE - RAISE NOTICE 'Creating type account_class_initialization_data_type'; -END IF; - -SELECT COUNT(1) INTO v - FROM pg_catalog.pg_type typ - INNER JOIN pg_catalog.pg_namespace nsp - ON nsp.oid = typ.typnamespace - WHERE nsp.nspname = 'brmbar_privileged' - AND typ.typname='account_load_type'; - -IF v>0 THEN - RAISE NOTICE 'Changing type account_load_type'; - DROP TYPE brmbar_privileged.account_load_type CASCADE; -ELSE - RAISE NOTICE 'Creating type account_load_type'; -END IF; - -CREATE TYPE brmbar_privileged.account_load_type - AS ENUM ('by_id', 'by_barcode'); - -CREATE TYPE brmbar_privileged.account_class_initialization_data_type -AS ( - account_id INTEGER, --public.accounts.id%TYPE, - account_name TEXT, --public.accounts.id%TYPE, - account_acctype public.account_type, - currency_id INTEGER, --public.currencies.id%TYPE, - currency_name TEXT -); - -CREATE OR REPLACE FUNCTION public.account_class_initialization_data( - load_by brmbar_privileged.account_load_type, - i_id INTEGER, - i_barcode TEXT) -RETURNS brmbar_privileged.account_class_initialization_data_type -LANGUAGE plpgsql -AS -$$ -DECLARE - rv brmbar_privileged.account_class_initialization_data_type; -BEGIN - IF load_by = 'by_id' THEN - SELECT a.id, a.name, a.acctype, c.id, c.name - INTO STRICT rv.account_id, rv.account_name, rv.account_acctype, rv.currency_id, rv.currency_name - FROM public.accounts a JOIN public.currencies c ON a.currency = c.id - WHERE a.id = i_id; - ELSE -- by_barcode - SELECT a.id, a.name, a.acctype, c.id, c.name - INTO STRICT rv.account_id, rv.account_name, rv.account_acctype, rv.currency_id, rv.currency_name - FROM public.accounts a JOIN public.currencies c ON a.currency = c.id JOIN public.barcodes b ON b.account = a.id - WHERE b.barcode = i_barcode; - END IF; - -- - RETURN rv; -END; -$$; - - - -PERFORM brmbar_privileged.upgrade_schema_version_to(25); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/test--currency-rates.py b/brmbar3/test--currency-rates.py deleted file mode 100644 index 9ef93bb..0000000 --- a/brmbar3/test--currency-rates.py +++ /dev/null @@ -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() diff --git a/brmbar3/uklid-refill.sh b/brmbar3/uklid-refill.sh deleted file mode 100644 index 898a081..0000000 --- a/brmbar3/uklid-refill.sh +++ /dev/null @@ -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 diff --git a/brmbar3/uklid-watchdog.sh b/brmbar3/uklid-watchdog.sh deleted file mode 100644 index 7f86870..0000000 --- a/brmbar3/uklid-watchdog.sh +++ /dev/null @@ -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 -