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/INSTALL.md b/brmbar3/INSTALL.md deleted file mode 100644 index b9cc75c..0000000 --- a/brmbar3/INSTALL.md +++ /dev/null @@ -1,85 +0,0 @@ -BrmBar v3 Installation -====================== - -This is woefully incomplete; if you are deploying BrmBar, at this -point you will likely need to learn at least something aobut its -structure and internals. Some code modifications might even be -required too. Patches enhancing user configurability are welcome! - -Maybe some things are missing. Ask the developers if you get in trouble, -e.g. in #brmlab on FreeNode. - -Hardware Requirements ---------------------- - -* Display. Current UI is optimized for 4:3, 1024x768 display. -* Touchscreen. In emergency, you can use a mouse too, but it's - clumsy and scrolling is not too intuitive. -* Barcode reader. We want the kind that will behave as a HID device - and on scanning a barcode, it will send a CR-terminated scanned string. -* Physical keyboard stashed in vicinity will help. It is possible - to enter text (inventory names, receipt reasons) on the touchscreen, - but it's a bit frustrating. -* You will want to print a sheet of barcodes with names of all user - accounts; these will be then used by people to buy stuff using their - accounts - first scan barcode of the item, then scan your barcode, - voila. Scanning your barcode directly can bring the user to a screen - where they can see their credit and charge it too. See also USAGE. - -Software Requirements ---------------------- - -* Developed and tested on Debian, but should work on other systems too. -* Python 3. -* QT4 with Python bindings: - * QT4 with the "Declarative" module, e.g. libqt4-declarative package. - * The PySide Qt4 bindings, e.g. python3-pyside.qtdeclarative package. - * Installing the qtcreator program may be helpful for QML testing - and development. -* PostgreSQL with Python pindings: - * The database server itself, e.g. postgresql package. - * PsyCoPg2, e.g. python3-psycopg2 package. - -Ubuntu packages installation instructions ------------------------------------------ -* sudo apt-get install postgresql libqt4-declarative python3 python3-pyside.qtdeclarative python3-psycopg2 python3-pyqt4 - -Software Setup --------------- - -* Create psql user and `brmbar` database. - - brmuser@host:~> su postgres - postgres@host:/home/user> createuser -D brmuser - postgres@host:/home/user> su brmuser - brmuser@host:~> createdb brmbar - -* The SQL schema in file `SQL` contains the required SQL tables, - but also INSERTs that add some rows essential for proper operation; - base currency and two base accounts. You *will* want to tweak the - currency name; default is `Kč` (the Czech crown), replace it with - your currency symbol. Then do `git grep 'Kč'` and replace all other - occurences of `Kč` in brmbar source with your currency name. -* Load the SQL schema stored in file `SQL` in the database. - - brmuser@host:~/brmbar/brmbar3> psql brmbar - psql (9.1.8) - Type "help" for help. - - brmbar=# \i SQL - -* You should be able to fire up the GUI now and start entering data. - If you want to make sure all works as expected, execute the SQL - statements in file `SQL.test` (revisit for currency names too) which - will populate the database with a bit of sample data for testing. -* Regarding adding users at this point and for other usage instructions, - refer to the USAGE file. - -TODO: Mention the actual commands to execute. - -Troubleshooting ---------------- - -Assuming that you run brmbar from a terminal, if something gets -stuck, you can switch to the terminal by Alt-TAB, then kill brmbar -by the Ctrl-\ shortcut (sends SIGQUIT) and restart it. 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/README.md b/brmbar3/README.md deleted file mode 100644 index b113083..0000000 --- a/brmbar3/README.md +++ /dev/null @@ -1,81 +0,0 @@ -BrmBar v3 -========= - -BrmBar is a management system for running a tiny hackerspace shop -with self-service usage based on trust and support for user accounts -and inventory tracking. - -BrmBar offers a touchscreen-based user interface, identifies items -and users by barcodes scanned by a barcode reader, should run on any -decent Linux machine and stores its data in PostgreSQL database. - -Features --------- - -* Very simple user interface (using big touchscreen buttons and - barcode reader) that should enable even non-technical users to - do basic shopping with little to no training. -* Users may have their accounts they can load with money by - depositing larger sum of money in advance, then charging their - account when buying stuff. Of course, paying direct for cash - is also supported. -* Inventory and cash accounts are tracked so that you can make sure - there is no Club Mate mysteriously disappearing or if the amount - of cash in the cash box is not less than expected by the system. -* You can enter receipts for duct tapes and other necessities to be - financed by cash surplus generated by brmbar. -* Simple management operations (depositing and withdrawing money - from user accounts, entering receipts, stocking in new inventory) - can be also performed in the user interface even by non-technical - users with basic training. -* The database is based on the classical accounting paradigm. - This means no information is needlessly lost, you could even - make a GNUCash export and your accounting geeks will feel warm - and fuzzy. -* Multiple user interfaces available (and possible). The primary - user interface is based on QtQuick (Qt4 QML QtDeclarative). - -User Interfaces ---------------- - -These UIs are provided: - -* **brmbar-gui-qt4**: The default touchscreen-based UI. The Python side - provides an adapter object whose methods can be executed by the QML - code; ad-hoc directionary objects are used to exchange complex data - like account information. -* **brmbar-tui**: A trivial text-based "shell" UI that mimics a historic - interface used in the Brmlab hackerspace in the past. It supports only - selling items, querying item price and user account balance and - depositing money for the user accounts. -* **brmbar-cli**: A command-line interface intended for use in scripts - and remote usage when fixing problems. It is also meant to provide - advanced functionality like inventory revision that is too tedious - to implement in the Qt4 GUI and only the brmbar admins are expected - to do these tasks. -* **brmbar-web**: A simple read-only web interface to the stock list. - -TODO ----- - -* The user interface needs some improvements, mainly regarding - scrolling in large lists. -* The brmbar-cli.py admin script for advanced/remote management - operations is largely unfinished. In the meantime, you need to use - SQL statements, sorry. Or finish it yourself. :-) -* It is common to have two stashes of cash, one in a cash box - in the shop, another in a vault (sometimes called "overflow") - where extra cash is stored. The brmbar model supports this, - but UI support needs to be added. -* Bitcoin support, somehow... - -Some more TODO items may be listed in the GitHub issue tracker; -missing brmbar-gui-qt4 features are listed in the `TODO` file. - -Other Resources ---------------- - -See the INSTALL file for setup instructions and USAGE file for -basic usage instructions. The doc/architecture file describes -the brmbar object model and briefly explains the brmbar Python -package. diff --git a/brmbar3/SQL b/brmbar3/SQL index 04667f4..2865c02 100644 --- a/brmbar3/SQL +++ b/brmbar3/SQL @@ -1,11 +1,10 @@ -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 ( @@ -37,14 +36,10 @@ CREATE TABLE accounts ( currency INTEGER NOT NULL, FOREIGN KEY (currency) REFERENCES currencies (id), - acctype account_type NOT NULL, - - active BOOLEAN NOT NULL DEFAULT TRUE + acctype account_type 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'); -INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Excess', (SELECT id FROM currencies WHERE name='Kč'), 'income'); -INSERT INTO accounts (name, currency, acctype) VALUES ('BrmBar Deficit', (SELECT id FROM currencies WHERE name='Kč'), 'expense'); CREATE SEQUENCE barcodes_id_seq START WITH 1 INCREMENT BY 1; @@ -54,9 +49,6 @@ CREATE TABLE barcodes ( account INTEGER NOT NULL, FOREIGN KEY (account) REFERENCES accounts (id) ); --- Barcode for cash --- XXX Silently assume there is only one. -INSERT INTO barcodes (barcode, account) VALUES ('_cash_', (SELECT id FROM accounts WHERE acctype = 'cash')); CREATE SEQUENCE transactions_id_seq START WITH 1 INCREMENT BY 1; @@ -98,30 +90,3 @@ CREATE VIEW account_balances AS 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 - SELECT ts.id AS id, ts.transaction AS transaction, ts.account AS account, - (CASE WHEN ts.side = 'credit' THEN -ts.amount ELSE ts.amount END) AS amount, - a.currency AS currency, ts.memo AS memo - 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. -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 - FROM transactions AS t - LEFT JOIN (SELECT cts.amount AS credit_cash, cts.transaction AS cts_t - FROM transaction_nicesplits AS cts - LEFT JOIN accounts AS a ON a.id = cts.account OR a.id = cts.account - WHERE a.currency = (SELECT currency FROM accounts WHERE name = 'BrmBar Cash') - AND a.acctype IN ('cash', 'debt') - AND cts.amount < 0) credit ON cts_t = t.id - LEFT JOIN (SELECT dts.amount AS debit_cash, dts.transaction AS dts_t - FROM transaction_nicesplits AS dts - LEFT JOIN accounts AS a ON a.id = dts.account OR a.id = dts.account - 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; 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/TODO b/brmbar3/TODO index edfcc27..5afc2de 100644 --- a/brmbar3/TODO +++ b/brmbar3/TODO @@ -1,5 +1,26 @@ -This reprensents some generic features that would need to be implemented -in brmbar-gui-qt4 to have a fully fledged user interface. ++ Management view + + Stock management link + + User management link + + Bilance overview (Cash, Profit, Credit total) ++ Item picker from list ++ User management + + List of users + + Withdrawal of user credit ++ Numerical manual entry support + + Use for credit charge + + Use for withdrawal ++ Restocking view (Stock management) + + Item picker with edit button + + Item editor (name, buy price, sale price, quantity) + + Item-barcode assignment + + Support for adding new items ++ Alphanumeric manual entry support + + Use in item editor ++ Withdrawal for brmbar receipts + 1. User responsible + 2. Amount and description + +** At this point it should be good enough to deploy ** . User management - Add user diff --git a/brmbar3/USAGE.md b/brmbar3/USAGE.md deleted file mode 100644 index d749a3a..0000000 --- a/brmbar3/USAGE.md +++ /dev/null @@ -1,153 +0,0 @@ -Quick Guide ------------ - -* I want to buy for cash: I scan item's barcode, press **Pay by Cash** and pour - money into the cash box. - -* I want to buy from credit: I scan item's barcode, then my barcode. -(If you don't have your barcode printed out, you can also type your -username on a physical keyboard.) - -* I want to put money on credit: press **Charge**, I scan my barcode, -type some amount, press **Charge** and put money in the cash box. - - -Advanced Operations -------------------- - -* I want to withdraw funds from my (positive) credit: -Press **Management**, choose **User Mgmt**, scan your barcode, -press the Withdraw Amount and type the amount. Then take the money -from the cash box. - -* I want to stock in some inventory (that's been in brmbar before): -Press **Management**, **Stock Mgmt**, scan barcode of the item, edit -the purchase price (or also the selling price and label), press -**Restock** and enter the quantity of stocked in piece. Press **Save**. -Toss the bill (if possible with the current written date, to allow -pairing) to brmbar. - -* I want to stock in some new inventory: Press **Management**, **Stock -Mgmt**, press **Add new item**, enter the name, purchase and selling price, -press **Create**. Then press **Restock**, enter the quantity stocked in. -Scan the item's barcode and press **Save**. Toss the bill in brmbar. - -* I want to bill the brmbar with some small expenses like duct tape: -Press **Management** and **Receipt**. Press **Description** and write -a brief description of the bill. Press **Edit** near the **Money Amount** -and enter the amount. Scan *your* barcode. The operation is finished -by pressing **Create**. Toss bill (inscribed with the current date -to ease pairing) to brmbar. - - -General Notes -------------- - -The system expects that we take money from the cash box right away. -If you don't want to (or there is e.g. not enough money), put money -on your credit account instead (see above). Please always do that -(never *I'll remember and I'll take money later*) so that there is -a record that the cash box and system records are not in sync and -there are no irregularities. - -To enter text (or numbers too), you can use both the on-screen keyboard -and the physical keyboard nearby. - - -Administrative Usage --------------------- - -* The most common administrative action you will need to do is adding - new user (also called debt or credit) accounts. The GUI support for - this is not implemented yet, but the `brmbar-cli.py` UI allows it: - - ./brmbar-cli.py adduser joehacker - - Afterwards, print out a barcode saying "joehacker" and stick that - somewhere nearby; scanning that barcode will allow access to this - 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. - - -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'; - -* 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'; - -* 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; - -* 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; - 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 0e8afac..0000000 --- a/brmbar3/autostock.py +++ /dev/null @@ -1,42 +0,0 @@ -#! /usr/bin/env python3 - -import argparse -import brmbar -import math -from brmbar import Database - -def main(): - parser = argparse.ArgumentParser(usage = "File format: EAN amount total_price name, e.g. 4001242002377 6 167.40 Chio Tortillas") - parser.add_argument("filename") - args = parser.parse_args() - - db = Database.Database("dbname=brmbar") - shop = brmbar.Shop.new_with_defaults(db) - currency = shop.currency - - # ... - total = 0 - with open(args.filename) as fin: - for line in fin: - split = line.split(" ") - ean, amount, price_total, name = split[0], int(split[1]), float(split[2]), " ".join(split[3:]) - name = name.strip() - - price_buy = price_total / amount - acct = brmbar.Account.load_by_barcode(db, ean) - if not acct: - print("Creating account for EAN {} '{}'".format(ean, name)) - invcurr = brmbar.Currency.create(db, name) - acct = brmbar.Account.create(db, name, invcurr, "inventory") - acct.add_barcode(ean) - price_sell = max(math.ceil(price_buy * 1.15), price_buy) - acct.currency.update_sell_rate(currency, price_sell) - acct.currency.update_buy_rate(currency, price_buy) - cash = shop.buy_for_cash(acct, amount) - total += cash - print("Increased by {}, take {} from cashbox".format(amount, cash)) - print("Total is {}".format(total)) - -if __name__ == "__main__": - main() - diff --git a/brmbar3/brmbar-cli.py b/brmbar3/brmbar-cli.py index cc5c6e3..bf619b3 100755 --- a/brmbar3/brmbar-cli.py +++ b/brmbar3/brmbar-cli.py @@ -1,266 +1,61 @@ #!/usr/bin/python3 import sys - -from brmbar import Database +import psycopg2 import brmbar - -def help(): - print("""BrmBar v3 (c) Petr Baudis 2012-2013 - -Usage: brmbar-cli.py COMMAND ARGS... - -1. Commands pertaining the standard operation - showcredit USER - changecredit USER +-AMT - sellitem {USER|"cash"} ITEM +-AMT - You can use negative AMT to undo a sale. - restock ITEM AMT - userinfo USER - userlog USER TIMESTAMP - iteminfo ITEM - -2. Management commands - listusers - List all user accounts in the system. - listitems - List all item accounts in the system. - stats - A set of various balances as shown in the Management - 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 - -USER and ITEM may be barcodes or account ids. AMT may be -both positive and negative amount (big difference to other -user interfaces; you can e.g. undo a sale!). - -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 -""") - sys.exit(1) - - -def load_acct(inp): - acct = None - if inp.isdigit(): - acct = brmbar.Account.load(db, id = inp) - if acct is None: - acct = brmbar.Account.load_by_barcode(db, inp) - if acct is None: - print("Cannot map account " + inp, file=sys.stderr) - exit(1) - return acct - -def load_user(inp): - acct = load_acct(inp) - if acct.acctype != "debt": - print("Bad account " + inp + " type " + acct.acctype, file=sys.stderr) - exit(1) - return acct - -def load_item(inp): - acct = load_acct(inp) - if acct.acctype != "inventory": - print("Bad account " + inp + " type " + acct.acctype, file=sys.stderr) - 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") +db = psycopg2.connect("dbname=brmbar") shop = brmbar.Shop.new_with_defaults(db) currency = shop.currency -if len(sys.argv) <= 1: - help() +active_inv_item = None +active_credit = None +for line in sys.stdin: + barcode = line.rstrip() -if sys.argv[1] == "showcredit": - acct = load_user(sys.argv[2]) - print("{}: {}".format(acct.name, acct.negbalance_str())) + if barcode[0] == "$": + credits = {'$02': 20, '$05': 50, '$10': 100, '$20': 200, '$50': 500, '$1k': 1000} + credit = credits[barcode] + if credit is None: + print("Unknown barcode: " + barcode) + continue + print("CREDIT " + str(credit)) + active_inv_item = None + active_credit = credit + continue -elif sys.argv[1] == "changecredit": - acct = load_user(sys.argv[2]) - amt = int(sys.argv[3]) - if amt > 0: - shop.add_credit(credit = amt, user = acct) - elif amt < 0: - shop.withdraw_credit(credit = -amt, user = acct) - print("{}: {}".format(acct.name, acct.negbalance_str())) + if barcode == "SCR": + print("SHOW CREDIT") + active_inv_item = None + active_credit = None + continue -elif sys.argv[1] == "sellitem": - if sys.argv[2] == "cash": - uacct = shop.cash - else: - 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) + acct = brmbar.Account.load_by_barcode(db, barcode) + if acct is None: + print("Unknown barcode: " + barcode) + continue + + if acct.acctype == 'debt': + if active_inv_item is not None: + cost = shop.sell(item = active_inv_item, user = acct) + print("{} has bought {} for {} and now has {} balance".format(acct.name, active_inv_item.name, currency.str(cost), acct.negbalance_str())) + elif active_credit is not None: + shop.add_credit(credit = active_credit, user = acct) + print("{} has added {} credit and now has {} balance".format(acct.name, currency.str(active_credit), acct.negbalance_str())) else: - 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(iacct.name, iacct.balance_str())) + print("{} has {} balance".format(acct.name, acct.negbalance_str())) + active_inv_item = None + active_credit = None -elif sys.argv[1] == "userinfo": - acct = load_user(sys.argv[2]) - print("{} (id {}): {}".format(acct.name, acct.id, acct.negbalance_str())) + elif acct.acctype == 'inventory': + buy, sell = acct.currency.rates(currency) + print("{} costs {} with {} in stock".format(acct.name, currency.str(sell), int(acct.balance()))) + active_inv_item = acct + active_credit = None - 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())) - - (buy, sell) = acct.currency.rates(currency) - print("Buy: " + currency.str(buy) + " Sell: " + currency.str(sell)); - - 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] == "listusers": - for acct in shop.account_list("debt"): - print("{}\t{}\t{}".format(acct.name, acct.id, acct.negbalance_str())) - -elif sys.argv[1] == "listitems": - for acct in shop.account_list("inventory"): - 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())) - -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.") else: - for i in range(2, len(sys.argv), 2): - iacct = load_item(sys.argv[i]) - iamt = int(sys.argv[i+1]) - 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.") - - -elif sys.argv[1] == "inventory-interactive": - print("Inventory interactive mode. To exit interactive mode just enter empty barcode") - - while True: - 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())) - else: - print("No action needed, amount is correct.") - print("End of processing. Bye") - -elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash": - if (len(sys.argv) != 3): - print ("Invalid number of parameters, check your parameters.") - else: - print("Current Cash is : {}".format(shop.cash.balance_str())) - iamt = int(sys.argv[2]) - if shop.fix_cash(amount = iamt): - 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() + print("invalid account type {}".format(acct.acctype)) + active_inv_item = None + active_credit = None diff --git a/brmbar3/brmbar-gui-qt4.py b/brmbar3/brmbar-gui-qt4.py index 9ca11d0..878f04b 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,15 +8,6 @@ from brmbar import Database import brmbar -# User credit balance limit; sale will fail when balance is below this limit. -LIMIT_BALANCE = -200 -# When below this credit balance, an alert hook script (see below) is run. -ALERT_BALANCE = 0 -# This script is executed when a user is buying things and their balance is -# below LIMIT_BALANCE (with argument "limit") or below ALERT_BALANCE -# (with argument "alert"). -ALERT_SCRIPT = "./alert.sh" - class ShopAdapter(QtCore.QObject): """ Interface between QML and the brmbar package """ @@ -39,18 +29,6 @@ class ShopAdapter(QtCore.QObject): map["price"] = str(sell) return map - def acct_inventory_map2(self, acct): - buy, sell = 666, 666 - map = acct.__dict__.copy() - map["balance"] = "{:.0f}".format(666) - map["buy_price"] = str(buy) - map["price"] = str(sell) - return map - - def acct_cash_map(self, acct): - map = acct.__dict__.copy() - return map - def acct_map(self, acct): if acct is None: return None @@ -58,8 +36,6 @@ class ShopAdapter(QtCore.QObject): return self.acct_debt_map(acct) elif acct.acctype == "inventory": return self.acct_inventory_map(acct) - elif acct.acctype == "cash": - return self.acct_cash_map(acct) else: return None @@ -88,18 +64,6 @@ class ShopAdapter(QtCore.QObject): db.commit() return acct - @QtCore.Slot('QVariant', 'QVariant', result='QVariant') - def canSellItem(self, itemid, userid): - user = brmbar.Account.load(db, id = userid) - if -user.balance() > ALERT_BALANCE: - return True - elif -user.balance() > LIMIT_BALANCE: - subprocess.call(["sh", ALERT_SCRIPT, "alert"]) - return True - else: - subprocess.call(["sh", ALERT_SCRIPT, "limit"]) - return False - @QtCore.Slot('QVariant', 'QVariant', result='QVariant') def sellItem(self, itemid, userid): user = brmbar.Account.load(db, id = userid) @@ -115,7 +79,6 @@ class ShopAdapter(QtCore.QObject): @QtCore.Slot('QVariant', 'QVariant', result='QVariant') def chargeCredit(self, credit, userid): - subprocess.call(["sh", ALERT_SCRIPT, "charge"]) user = brmbar.Account.load(db, id = userid) shop.add_credit(credit = credit, user = user) balance = user.negbalance_str() @@ -130,19 +93,6 @@ class ShopAdapter(QtCore.QObject): db.commit() return balance - @QtCore.Slot('QVariant', 'QVariant', 'QVariant', result='QVariant') - def newTransfer(self, uidfrom, uidto, amount): - ufrom = brmbar.Account.load(db, id=uidfrom) - uto = brmbar.Account.load(db, id=uidto) - shop.transfer_credit(ufrom, uto, amount = amount) - db.commit() - return currency.str(float(amount)) - - @QtCore.Slot('QVariant', result='QVariant') - def balance_user(self, userid): - user = brmbar.Account.load(db, id=userid) - return user.negbalance_str() - @QtCore.Slot(result='QVariant') def balance_cash(self): balance = shop.cash.balance_str() @@ -170,9 +120,9 @@ class ShopAdapter(QtCore.QObject): db.commit() return alist - @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+"%%") ] + @QtCore.Slot(result='QVariant') + def itemList(self): + alist = [ self.acct_inventory_map(a) for a in shop.account_list("inventory") ] db.commit() return alist diff --git a/brmbar3/brmbar-gui-qt4/BarButton.qml b/brmbar3/brmbar-gui-qt4/BarButton.qml index 8718312..b5d8a30 100644 --- a/brmbar3/brmbar-gui-qt4/BarButton.qml +++ b/brmbar3/brmbar-gui-qt4/BarButton.qml @@ -1,3 +1,4 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 Rectangle { @@ -9,7 +10,7 @@ Rectangle { property string text: "Button" property int fontSize: 0.768 * 60 - property string btnColor: "#aaaaaa" + property variant btnColor: "#aaaaaa" signal buttonClick onButtonClick: { /* Supplied by component user. */ } diff --git a/brmbar3/brmbar-gui-qt4/BarClock.qml b/brmbar3/brmbar-gui-qt4/BarClock.qml index a444a2e..c348457 100644 --- a/brmbar3/brmbar-gui-qt4/BarClock.qml +++ b/brmbar3/brmbar-gui-qt4/BarClock.qml @@ -1,3 +1,4 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 Rectangle { @@ -5,8 +6,8 @@ Rectangle { width: 320 height: 65 property variant now: new Date() - property string textColor: "#000000" - property real textSize: 0.768 * 16 + property variant textColor: "#000000" + property variant textSize: 0.768 * 16 Timer { id: clockUpdater interval: 1000 // update clock every second diff --git a/brmbar3/brmbar-gui-qt4/BarKeyPad.qml b/brmbar3/brmbar-gui-qt4/BarKeyPad.qml index c613f4f..bed9f02 100644 --- a/brmbar3/brmbar-gui-qt4/BarKeyPad.qml +++ b/brmbar3/brmbar-gui-qt4/BarKeyPad.qml @@ -4,6 +4,6 @@ BarKeyboard { keys: "0123456789=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/ItemEdit.qml b/brmbar3/brmbar-gui-qt4/ItemEdit.qml index f03504c..d5ecb27 100644 --- a/brmbar3/brmbar-gui-qt4/ItemEdit.qml +++ b/brmbar3/brmbar-gui-qt4/ItemEdit.qml @@ -1,14 +1,16 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page anchors.fill: parent - property string item_name: item_name_pad.enteredText - property string dbid: "" + property variant item_name: item_name_pad.enteredText + property variant dbid: "" property variant info: "" - property string buy_price: item_buyprice_pad.enteredText - property string price: item_sellprice_pad.enteredText + property variant buy_price: item_buyprice_pad.enteredText + property variant price: item_sellprice_pad.enteredText property string barcode: "" state: "normal" @@ -26,7 +28,7 @@ Item { /* TODO: Allow override. */ return } - if (info.dbid === "") { + if (info.dbid == "") { status_text.setStatus("Press [Create] first", "#ff4444") return } @@ -58,7 +60,7 @@ Item { BarButton { id: item_name_edit - x: 790 + x: 534 y: 0 width: 240 height: 60 @@ -113,7 +115,7 @@ Item { BarButton { id: item_buyprice_edit - x: 790 + x: 534 y: 0 width: 240 height: 60 @@ -168,7 +170,7 @@ Item { BarButton { id: item_sellprice_edit - x: 790 + x: 534 y: 0 width: 240 height: 60 @@ -223,7 +225,7 @@ Item { BarButton { id: item_balance_restock - x: 790 + x: 534 y: 0 width: 240 height: 60 @@ -304,46 +306,46 @@ Item { BarButton { id: save x: 65 - y: 838 + y: 582 width: 360 text: dbid == "" ? "Create" : "Save" onButtonClick: { - var xi = info; - xi["name"] = page.item_name; - xi["buy_price"] = page.buy_price; - xi["price"] = page.price; - info = xi + var xi = info; + xi["name"] = page.item_name; + xi["buy_price"] = page.buy_price; + xi["price"] = page.price; + info = xi - var res; - if (dbid == "") { - res = shop.newItem(info) - if (!res) { - status_text.setStatus("Please fill all values first.", "#ff4444") - return - } - } else { - res = shop.saveItem(dbid, info) - } + var res; + if (dbid == "") { + res = shop.newItem(info) + if (!res) { + status_text.setStatus("Please fill all values first.", "#ff4444") + return + } + } else { + res = shop.saveItem(dbid, info) + } - if (res.cost) { - status_text.setStatus((dbid == "" ? "Stocked!" : "Restocked!") + " Take " + res.cost + " from the money box.", "#ffff7c") - } else { - status_text.setStatus(dbid == "" ? "Item created" : "Changes saved", "#ffff7c") - } + if (res.cost) { + status_text.setStatus((dbid == "" ? "Stocked!" : "Restocked!") + " Take " + res.cost + " from the money box.", "#ffff7c") + } else { + status_text.setStatus(dbid == "" ? "Item created" : "Changes saved", "#ffff7c") + } - if (dbid == "") { - dbid = res.dbid - xi = info; xi["dbid"] = page.dbid; info = xi - } else { - loadPage("StockMgmt") - } + if (dbid == "") { + dbid = res.dbid + var xi = info; xi["dbid"] = page.dbid; info = xi + } else { + loadPage("StockMgmt") + } } } BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Cancel" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/ItemInfo.qml b/brmbar3/brmbar-gui-qt4/ItemInfo.qml index 11e959d..8d765b0 100644 --- a/brmbar3/brmbar-gui-qt4/ItemInfo.qml +++ b/brmbar3/brmbar-gui-qt4/ItemInfo.qml @@ -1,12 +1,14 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page anchors.fill: parent - property string name: "" - property string dbid: "" - property string price: "" + property variant name: "" + property variant dbid: "" + property variant price: "" Text { id: item_name @@ -23,7 +25,7 @@ Item { Text { id: text3 - x: 867 + x: 611 y: 156 height: 160 width: 348 @@ -50,20 +52,12 @@ Item { status_text.setStatus("Unknown barcode", "#ff4444") return } - if (acct.acctype !== "debt" && acct.acctype !== "cash") { + if (acct.acctype != "debt") { loadPageByAcct(acct) return } - - 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") - } + var balance = shop.sellItem(dbid, acct.id) + status_text.setStatus("Sold! "+acct.name+"'s credit is "+balance+".", "#ffff7c") loadPage("MainPage") } } @@ -71,7 +65,7 @@ Item { BarButton { id: pay_cash x: 65 - y: 838 + y: 582 width: 360 text: "Pay by cash" fontSize: 0.768 * 60 @@ -84,8 +78,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Cancel" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/MainPage.qml b/brmbar3/brmbar-gui-qt4/MainPage.qml index d11fd0f..5c6b5b1 100644 --- a/brmbar3/brmbar-gui-qt4/MainPage.qml +++ b/brmbar3/brmbar-gui-qt4/MainPage.qml @@ -1,4 +1,6 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page @@ -25,7 +27,7 @@ Item { BarButton { x: 65 - y: 838 + y: 582 width: 360 text: "Charge" onButtonClick: { @@ -33,31 +35,14 @@ Item { } } - BarButton { - x: 450 - y: 838 - width: 360 - text: "Transfer" - onButtonClick: { - loadPage("Transfer") - } - } - BarButton { id: management - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Management" onButtonClick: { loadPage("Management") } } - - BarButton { - x: 65 - y: 438 - width: 1150 - text: "* Za uklid brmlabu vam nabijeme kredit. *" - } } diff --git a/brmbar3/brmbar-gui-qt4/Management.qml b/brmbar3/brmbar-gui-qt4/Management.qml index 6cb416a..e316ca0 100644 --- a/brmbar3/brmbar-gui-qt4/Management.qml +++ b/brmbar3/brmbar-gui-qt4/Management.qml @@ -1,4 +1,6 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page @@ -63,7 +65,7 @@ Item { Text { id: credit_name - x: 791 + x: 535 y: 156 width: 337 height: 160 @@ -76,7 +78,7 @@ Item { Text { id: credit_amount - x: 961 + x: 705 y: 156 height: 160 width: 254 @@ -89,7 +91,7 @@ Item { Text { id: inv_name - x: 791 + x: 535 y: 266 width: 337 height: 160 @@ -102,7 +104,7 @@ Item { Text { id: inv_amount - x: 961 + x: 705 y: 266 height: 160 width: 254 @@ -116,7 +118,7 @@ Item { BarButton { id: stock_manager x: 65 - y: 686 + y: 430 width: 360 text: "Stock Mgmt" onButtonClick: { @@ -126,8 +128,8 @@ Item { BarButton { id: user_manager - x: 855 - y: 686 + x: 599 + y: 430 width: 360 text: "User Mgmt" onButtonClick: { @@ -138,7 +140,7 @@ Item { BarButton { id: select_item x: 65 - y: 838 + y: 582 width: 360 text: "Receipt" onButtonClick: { @@ -148,8 +150,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Main Screen" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/Receipt.qml b/brmbar3/brmbar-gui-qt4/Receipt.qml index 76855ac..5e7288b 100644 --- a/brmbar3/brmbar-gui-qt4/Receipt.qml +++ b/brmbar3/brmbar-gui-qt4/Receipt.qml @@ -1,12 +1,14 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page anchors.fill: parent property variant user - property string description: item_name_pad.enteredText - property string amount: amount_pad.enteredText + property variant description: item_name_pad.enteredText + property variant amount: amount_pad.enteredText state: "normal" @@ -47,7 +49,7 @@ Item { BarButton { id: description_edit - x: 847 + x: 591 y: 0 width: 300 height: 60 @@ -102,7 +104,7 @@ Item { BarButton { id: amount_edit - x: 906 + x: 650 y: 0 width: 240 height: 60 @@ -148,7 +150,7 @@ Item { BarButton { id: save x: 65 - y: 838 + y: 582 width: 360 text: "Create" onButtonClick: { @@ -169,8 +171,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Cancel" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/StockMgmt.qml b/brmbar3/brmbar-gui-qt4/StockMgmt.qml index 4841865..68a6838 100644 --- a/brmbar3/brmbar-gui-qt4/StockMgmt.qml +++ b/brmbar3/brmbar-gui-qt4/StockMgmt.qml @@ -1,4 +1,6 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page @@ -6,8 +8,6 @@ Item { property variant item_list_model - state: "normal" - BarcodeInput { color: "#00ff00" /* just for debugging */ onAccepted: { @@ -29,8 +29,8 @@ Item { id: item_list_container x: 65 y: 166 - width: 1155 - height: 656 + width: 899 + height: 400 ListView { id: item_list @@ -49,7 +49,7 @@ Item { Text { anchors.verticalCenter: parent.verticalCenter - x: 556 + x: 300 width: 254 color: "#ffff7c" text: modelData.price @@ -59,7 +59,7 @@ Item { BarButton { anchors.verticalCenter: parent.verticalCenter - x: 856 + x: 600 width: 240 height: 68 text: "Edit" @@ -81,14 +81,12 @@ Item { } BarButton { - id: new_item + id: add_item x: 65 - y: 838 - width: 281 - height: 83 - text: "New Item" + y: 582 + width: 360 + text: "Add Item" fontSize: 0.768 * 60 - visible: page.state == "normal" onButtonClick: { loadPage("ItemEdit", { dbid: "" }) } @@ -96,96 +94,16 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Main Screen" onButtonClick: { - if (page.state == "search") - page.state = "normal" - else - loadPage("MainPage") + loadPage("MainPage") } } - BarButton { - id: search_button - x: 353 - y: 838 - text: "Search" - visible: page.state == "normal" - onButtonClick: { page.state = "search" } - } - - BarKeyPad { - id: search_pad - x: 193 - y: 554 - opacity: 0 - } - - Text { - id: search_text - x: 65 - y: 602 - color: "#ffff7c" - text: search_pad.enteredText - visible: page.state == "search" - font.pixelSize: 0.768 * 46 - opacity: 0 - } - - BarButton { - id: query_button - x: 353 - y: 838 - text: "Search" - visible: page.state == "search" - onButtonClick: { - page.item_list_model = shop.itemList(search_pad.enteredText) - item_list.model = page.item_list_model - } - } - - states: [ - State { - name: "normal" - }, - State { - name: "search" - - PropertyChanges { - target: item_list_container - x: 66 - y: 166 - width: 1155 - height: 348 - } - - PropertyChanges { - target: search_pad - x: 83 - y: 514 - opacity: 1 - } - - PropertyChanges { - target: cancel - text: "Back" - } - - PropertyChanges { - target: search_text - x: 65 - y: 838 - width: 528 - height: 83 - opacity: 1 - } - } - ] - Component.onCompleted: { - item_list_model = shop.itemList("") + item_list_model = shop.itemList() } } 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/UserInfo.qml b/brmbar3/brmbar-gui-qt4/UserInfo.qml index 80e0887..ab4b0c2 100644 --- a/brmbar3/brmbar-gui-qt4/UserInfo.qml +++ b/brmbar3/brmbar-gui-qt4/UserInfo.qml @@ -1,12 +1,13 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 Item { id: page anchors.fill: parent - property string name: "" - property string dbid: "" - property string negbalance: "" + property variant name: "" + property variant dbid: "" + property variant negbalance: "" Text { id: item_name @@ -42,7 +43,7 @@ Item { status_text.setStatus("Unknown barcode", "#ff4444") return } - if (acct.acctype === "recharge") { + if (acct.acctype == "recharge") { loadPage("ChargeCredit", { "username": name, "userdbid": dbid, "amount": acct.amount }) return } @@ -54,7 +55,7 @@ Item { BarButton { id: charge_credit x: 65 - y: 838 + y: 582 width: 360 text: "Charge" fontSize: 0.768 * 60 @@ -65,8 +66,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Main Screen" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/UserMgmt.qml b/brmbar3/brmbar-gui-qt4/UserMgmt.qml index da1c7c8..b77e840 100644 --- a/brmbar3/brmbar-gui-qt4/UserMgmt.qml +++ b/brmbar3/brmbar-gui-qt4/UserMgmt.qml @@ -1,4 +1,6 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page @@ -15,7 +17,7 @@ Item { status_text.setStatus("Unknown barcode", "#ff4444") return } - if (acct.acctype !== "debt") { + if (acct.acctype != "debt") { loadPageByAcct(acct) return } @@ -28,8 +30,8 @@ Item { id: user_list_container x: 65 y: 166 - width: 1155 - height: 656 + width: 899 + height: 400 ListView { id: user_list @@ -48,7 +50,7 @@ Item { Text { anchors.verticalCenter: parent.verticalCenter - x: 556 + x: 300 width: 254 color: "#ffff7c" text: modelData.negbalance_str @@ -58,7 +60,7 @@ Item { BarButton { anchors.verticalCenter: parent.verticalCenter - x: 856 + x: 600 width: 240 height: 68 text: "Withdraw" @@ -82,7 +84,7 @@ Item { BarButton { id: add_user x: 65 - y: 838 + y: 582 width: 360 text: "Add User" fontSize: 0.768 * 60 @@ -91,8 +93,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Main Screen" onButtonClick: { diff --git a/brmbar3/brmbar-gui-qt4/Withdraw.qml b/brmbar3/brmbar-gui-qt4/Withdraw.qml index 2d8c49b..a936f7e 100644 --- a/brmbar3/brmbar-gui-qt4/Withdraw.qml +++ b/brmbar3/brmbar-gui-qt4/Withdraw.qml @@ -1,12 +1,14 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 +import QtQuick 1.0 Item { id: page anchors.fill: parent - property string username: "" - property string userdbid: "" - property string amount: withdraw_pad.enteredText + property variant username: "" + property variant userdbid: "" + property variant amount: withdraw_pad.enteredText Text { id: item_name @@ -37,7 +39,7 @@ Item { BarTextHint { x: 65 - y: 686 + y: 430 hint_goal: (parent.username ? "" : parent.amount ? "Withdraw:" : "Withdraw amount?") hint_action: (parent.username ? (parent.amount ? "" : "(or scan barcode now)") : "Scan barcode now") } @@ -77,7 +79,7 @@ Item { BarButton { id: withdraw_button x: 65 - y: 838 + y: 582 width: 360 text: "Withdraw" fontSize: 0.768 * 60 @@ -89,8 +91,8 @@ Item { BarButton { id: cancel - x: 855 - y: 838 + x: 599 + y: 582 width: 360 text: "Cancel" onButtonClick: { @@ -100,17 +102,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-gui-qt4/brmbar-gui-qt4.qmlproject.user b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user index 0a23204..eeee7ab 100644 --- a/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user +++ b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user @@ -1,6 +1,6 @@ - + ProjectExplorer.Project.ActiveTarget @@ -112,7 +112,7 @@ ProjectExplorer.Project.Updater.EnvironmentId - {524378aa-09e0-4345-892b-1bd47313bcaf} + {a277f310-b549-4ad7-87ca-cd03f76f19ff} ProjectExplorer.Project.Updater.FileVersion diff --git a/brmbar3/brmbar-gui-qt4/main.qml b/brmbar3/brmbar-gui-qt4/main.qml index 9eefbc6..5c43528 100644 --- a/brmbar3/brmbar-gui-qt4/main.qml +++ b/brmbar3/brmbar-gui-qt4/main.qml @@ -1,3 +1,4 @@ +// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 BasePage { @@ -16,11 +17,11 @@ BasePage { } function loadPageByAcct(acct) { - if (acct.acctype === "inventory") { + if (acct.acctype == "inventory") { loadPage("ItemInfo", { name: acct["name"], dbid: acct["id"], price: acct["price"] }) - } else if (acct.acctype === "debt") { + } else if (acct.acctype == "debt") { loadPage("UserInfo", { name: acct["name"], dbid: acct["id"], negbalance: acct["negbalance"] }) - } else if (acct.acctype === "recharge") { + } else if (acct.acctype == "recharge") { loadPage("ChargeCredit", { amount: acct["amount"] }) } } diff --git a/brmbar3/brmbar-tui.py b/brmbar3/brmbar-tui.py deleted file mode 100755 index 00bdf44..0000000 --- a/brmbar3/brmbar-tui.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/python3 - -import sys - -from brmbar import Database - -import brmbar - -db = Database.Database("dbname=brmbar") -shop = brmbar.Shop.new_with_defaults(db) -currency = shop.currency - -active_inv_item = None -active_credit = None - -for line in sys.stdin: - barcode = line.rstrip() - - if barcode[0] == "$": - credits = {'$02': 20, '$05': 50, '$10': 100, '$20': 200, '$50': 500, '$1k': 1000} - credit = credits[barcode] - if credit is None: - print("Unknown barcode: " + barcode) - continue - print("CREDIT " + str(credit)) - active_inv_item = None - active_credit = credit - continue - - if barcode == "SCR": - print("SHOW CREDIT") - active_inv_item = None - active_credit = None - continue - - acct = brmbar.Account.load_by_barcode(db, barcode) - if acct is None: - print("Unknown barcode: " + barcode) - continue - - if acct.acctype == 'debt': - if active_inv_item is not None: - cost = shop.sell(item = active_inv_item, user = acct) - print("{} has bought {} for {} and now has {} balance".format(acct.name, active_inv_item.name, currency.str(cost), acct.negbalance_str())) - elif active_credit is not None: - shop.add_credit(credit = active_credit, user = acct) - print("{} has added {} credit and now has {} balance".format(acct.name, currency.str(active_credit), acct.negbalance_str())) - else: - print("{} has {} balance".format(acct.name, acct.negbalance_str())) - active_inv_item = None - active_credit = None - - elif acct.acctype == 'inventory': - buy, sell = acct.currency.rates(currency) - print("{} costs {} with {} in stock".format(acct.name, currency.str(sell), int(acct.balance()))) - active_inv_item = acct - active_credit = None - - else: - print("invalid account type {}".format(acct.acctype)) - active_inv_item = None - active_credit = None diff --git a/brmbar3/brmbar-web.py b/brmbar3/brmbar-web.py deleted file mode 100755 index 5d84378..0000000 --- a/brmbar3/brmbar-web.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/python - -import sys - -from brmbar import Database - -import brmbar - -from flask import * -app = Flask(__name__) -#app.debug = True - -@app.route('/stock/') -def stock(show_all=False): - # TODO: Use a fancy template. - # FIXME: XSS protection. - response = '' - for a in shop.account_list("inventory"): - style = '' - balance = a.balance() - if balance == 0: - if not show_all: - continue - style = 'color: grey; font-style: italic' - elif balance < 0: - style = 'color: red' - response += '' % (style, a.id, a.name, balance) - response += '
IdItem NameBal.
%d%s%d
' - if show_all: - response += '

(hide out-of-stock items)

' - else: - response += '

(show all items)

' - return response - -@app.route('/stock/all') -def stockall(): - return stock(show_all=True) - - -db = Database.Database("dbname=brmbar") -shop = brmbar.Shop.new_with_defaults(db) -currency = shop.currency - -if __name__ == '__main__': - app.run(host='0.0.0.0') diff --git a/brmbar3/brmbar/Account.py b/brmbar3/brmbar/Account.py index 215df11..29dd79e 100644 --- a/brmbar3/brmbar/Account.py +++ b/brmbar3/brmbar/Account.py @@ -40,22 +40,16 @@ class Account: @classmethod def create(cls, db, name, currency, acctype): """ Constructor for new account """ - # id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype]) - id = db.execute_and_fetch("SELECT public.create_account(%s, %s, %s)", [name, currency.id, acctype]) - # id = id[0] + id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype]) + id = id[0] return cls(db, name = name, id = id, currency = currency, acctype = acctype) def balance(self): - bal = self.db.execute_and_fetch( - "SELECT public.compute_account_balance(%s)", - [self.id] - )[0] - return bal - #debit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'debit']) - #debit = debit[0] or 0 - #credit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'credit']) - #credit = credit[0] or 0 - #return debit - credit + debit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'debit']) + debit = debit[0] or 0 + credit = self.db.execute_and_fetch("SELECT SUM(amount) FROM transaction_splits WHERE account = %s AND side = %s", [self.id, 'credit']) + credit = credit[0] or 0 + return debit - credit def balance_str(self): return self.currency.str(self.balance()) @@ -74,11 +68,9 @@ class Account: self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, self.id, amount, memo]) def add_barcode(self, barcode): - # self.db.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode]) - self.db.execute("SELECT public.add_barcode_to_account(%s, %s)", [self.id, barcode]) + self.db.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode]) self.db.commit() def rename(self, name): - # self.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id]) - self.db.execute("SELECT public.rename_account(%s, %s)", [self.id, name]) + self.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id]) self.name = name diff --git a/brmbar3/brmbar/Currency.py b/brmbar3/brmbar/Currency.py index 29da4a3..b382d77 100644 --- a/brmbar3/brmbar/Currency.py +++ b/brmbar3/brmbar/Currency.py @@ -1,4 +1,3 @@ -# vim: set fileencoding=utf8 class Currency: """ Currency @@ -31,34 +30,15 @@ class Currency: @classmethod def create(cls, db, name): """ Constructor for new currency """ - # id = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name]) - id = db.execute_and_fetch("SELECT public.create_currency(%s)", [name]) - # id = id[0] + id = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name]) + id = id[0] return cls(db, name = name, id = id) def rates(self, other): """ Return tuple ($buy, $sell) of rates of $self in relation to $other (brmbar.Currency): $buy is the price of $self in means of $other when buying it (into brmbar) $sell is the price of $self in means of $other when selling it (from brmbar) """ - # buy rate - res = self.db.execute_and_fetch("SELECT public.find_buy_rate(%s, %s)",[self.id, other.id]) - if res is None: - raise NameError("Something fishy in find_buy_rate."); - buy = res[0] - if buy < 0: - raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name()) - # sell rate - res = self.db.execute_and_fetch("SELECT public.find_sell_rate(%s, %s)",[self.id, other.id]) - if res is None: - raise NameError("Something fishy in find_sell_rate."); - sell = res[0] - if sell < 0: - raise NameError("Currency.rate(): Unknown conversion " + self.name() + " to " + other.name()) - return (buy, sell) - - def rates2(self, other): - # the original code for compare testing res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [self.id, other.id]) if res is None: raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name()) @@ -73,7 +53,6 @@ class Currency: return (buy, sell) - def convert(self, amount, target): res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [target.id, self.id]) if res is None: @@ -89,10 +68,6 @@ class Currency: return "{:.2f} {}".format(amount, self.name) def update_sell_rate(self, target, rate): - # self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [self.id, target.id, rate, "source_to_target"]) - self.db.execute("SELECT public.update_currency_sell_rate(%s, %s, %s)", - [self.id, target.id, rate]) + self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [self.id, target.id, rate, "source_to_target"]) def update_buy_rate(self, source, rate): - # self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [source.id, self.id, rate, "target_to_source"]) - self.db.execute("SELECT public.update_currency_buy_rate(%s, %s, %s)", - [source.id, self.id, rate]) + self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [source.id, self.id, rate, "target_to_source"]) diff --git a/brmbar3/brmbar/Database.py b/brmbar3/brmbar/Database.py index d0202d4..e5962ab 100644 --- a/brmbar3/brmbar/Database.py +++ b/brmbar3/brmbar/Database.py @@ -36,12 +36,6 @@ class Database: else: cur.execute(query, attrs) return cur - 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: print("Sleeping: level %s (%s) @%s" % ( level, error, time.strftime("%Y%m%d %a %I:%m %p") diff --git a/brmbar3/brmbar/Shop.py b/brmbar3/brmbar/Shop.py index e7dbc6c..1623cac 100644 --- a/brmbar3/brmbar/Shop.py +++ b/brmbar3/brmbar/Shop.py @@ -7,141 +7,75 @@ class Shop: Business logic so that only interaction is left in the hands of the frontend scripts. """ - def __init__(self, db, currency, profits, cash, excess, deficit): + def __init__(self, db, currency, profits, cash): self.db = db 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): 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")) + cash = Account.load(db, name = "BrmBar Cash")) def sell(self, item, user, amount = 1): - # Call the stored procedure for the sale - cost = self.db.execute_and_fetch( - "SELECT public.sell_item(%s, %s, %s, %s, %s)", - [item.id, amount, user.id, self.currency.id, f"BrmBar sale of {amount}x {item.name} to {user.name}"] - )[0]#[0] - - self.db.commit() - return cost # Sale: Currency conversion from item currency to shop currency - #(buy, sell) = item.currency.rates(self.currency) - #cost = amount * sell - #profit = amount * (sell - buy) + (buy, sell) = item.currency.rates(self.currency) + cost = amount * sell + profit = amount * (sell - buy) - #transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name)) - #item.credit(transaction, amount, user.name) - #user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account - #self.profits.debit(transaction, profit, "Margin on " + item.name) - #self.db.commit() - #return cost + transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name)) + item.credit(transaction, amount, user.name) + user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account + self.profits.debit(transaction, profit, "Margin on " + item.name) + self.db.commit() + + return cost def sell_for_cash(self, item, amount = 1): - cost = self.db.execute_and_fetch( - "SELECT public.sell_item_for_cash(%s, %s, %s, %s, %s)", - [item.id, amount, user.id, self.currency.id, f"BrmBar sale of {amount}x {item.name} for cash"] - )[0]#[0] - - self.db.commit() - return cost - ## Sale: Currency conversion from item currency to shop currency - #(buy, sell) = item.currency.rates(self.currency) - #cost = amount * sell - #profit = amount * (sell - buy) - - #transaction = self._transaction(description = "BrmBar sale of {}x {} for cash".format(amount, item.name)) - #item.credit(transaction, amount, "Cash") - #self.cash.debit(transaction, cost, item.name) - #self.profits.debit(transaction, profit, "Margin on " + item.name) - #self.db.commit() - - #return cost - - def undo_sale(self, item, user, amount = 1): - # Undo sale; rarely needed - #(buy, sell) = item.currency.rates(self.currency) - #cost = amount * sell - #profit = amount * (sell - buy) - - #transaction = self._transaction(responsible = user, description = "BrmBar sale UNDO of {}x {} to {}".format(amount, item.name, user.name)) - #item.debit(transaction, amount, user.name + " (sale undo)") - #user.credit(transaction, cost, item.name + " (sale undo)") - #self.profits.credit(transaction, profit, "Margin repaid on " + item.name) - # Call the stored procedure for undoing a sale - cost = self.db.execute_and_fetch( - "SELECT public.undo_sale_of_item(%s, %s, %s, %s)", - [item.id, amount, user.id, user.currency.id, f"BrmBar sale UNDO of {amount}x {item.name} to {user.name}"] - )[0]#[0] + # 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 add_credit(self, credit, user): - self.db.execute_and_fetch( - "SELECT public.add_credit(%s, %s, %s, %s)", - [self.cash.id, credit, user.id, user.name] - ) + transaction = self._transaction(responsible = user, description = "BrmBar credit replenishment for " + user.name) + self.cash.debit(transaction, credit, user.name) + user.credit(transaction, credit, "Credit replenishment") self.db.commit() - #transaction = self._transaction(responsible = user, description = "BrmBar credit replenishment for " + user.name) - #self.cash.debit(transaction, credit, user.name) - #user.credit(transaction, credit, "Credit replenishment") - #self.db.commit() - def withdraw_credit(self, credit, user): - self.db.execute_and_fetch( - "SELECT public.withdraw_credit(%s, %s, %s, %s)", - [self.cash.id, credit, user.id, user.name] - ) + transaction = self._transaction(responsible = user, description = "BrmBar credit withdrawal for " + user.name) + self.cash.credit(transaction, credit, user.name) + user.debit(transaction, credit, "Credit withdrawal") self.db.commit() - #transaction = self._transaction(responsible = user, description = "BrmBar credit withdrawal for " + user.name) - #self.cash.credit(transaction, credit, user.name) - #user.debit(transaction, credit, "Credit withdrawal") - #self.db.commit() - - def transfer_credit(self, userfrom, userto, amount): - self.db.execute_and_fetch( - "SELECT public.transfer_credit(%s, %s, %s, %s)", - [self.cash.id, credit, user.id, user.name] - ) - self.db.commit() - #self.add_credit(amount, userto) - #self.withdraw_credit(amount, userfrom) def buy_for_cash(self, item, amount = 1): - cost = self.db.execute_and_fetch( - "SELECT public.buy_for_cash(%s, %s, %s, %s, %s)", - [self.cash.id, item.id, amount, self.currency.id, item.name] - )[0] # Buy: Currency conversion from item currency to shop currency - #(buy, sell) = item.currency.rates(self.currency) - #cost = amount * buy + (buy, sell) = item.currency.rates(self.currency) + cost = amount * buy - #transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name)) - #item.debit(transaction, amount, "Cash") - #self.cash.credit(transaction, cost, item.name) + transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name)) + item.debit(transaction, amount, "Cash") + self.cash.credit(transaction, cost, item.name) self.db.commit() return cost def receipt_to_credit(self, user, credit, description): - #transaction = self._transaction(responsible = user, description = "Receipt: " + description) - #self.profits.credit(transaction, credit, user.name) - #user.credit(transaction, credit, "Credit from receipt: " + description) - self.db.execute_and_fetch( - "SELECT public.buy_for_cash(%s, %s, %s, %s, %s)", - [self.profits.id, user.id, user.name, credit, description] - )[0] + transaction = self._transaction(responsible = user, description = "Receipt: " + description) + self.profits.credit(transaction, credit, user.name) + user.credit(transaction, credit, "Credit from receipt: " + description) self.db.commit() def _transaction(self, responsible = None, description = None): @@ -150,7 +84,7 @@ class Shop: transaction = transaction[0] return transaction - def credit_balance(self, overflow=None): + def credit_balance(self): # We assume all debt accounts share a currency sumselect = """ SELECT SUM(ts.amount) @@ -158,17 +92,14 @@ class Shop: LEFT JOIN transaction_splits AS ts ON a.id = ts.account WHERE a.acctype = %s AND ts.side = %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)) + def credit_negbalance_str(self): + return self.currency.str(-self.credit_balance()) -# XXX causing extra heavy delay ( thousands of extra SQL queries ), disabled def inventory_balance(self): balance = 0 # Each inventory account has its own currency, @@ -181,116 +112,15 @@ class Shop: # might have been bought for a different price! Therefore, # we need to replace the command below with a complex SQL # statement that will... ugh, accounting is hard! - b = inv.balance() * inv.currency.rates(self.currency)[0] - # if b != 0: - # print(str(b) + ',' + inv.name) - balance += b + balance += inv.currency.convert(inv.balance(), self.currency) return balance - -# XXX bypass hack def inventory_balance_str(self): - # return self.currency.str(self.inventory_balance()) - return "XXX" + return self.currency.str(self.inventory_balance()) - def account_list(self, acctype, like_str="%%"): + def account_list(self, acctype): """list all accounts (people or items, as per acctype)""" accts = [] - cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s AND name ILIKE %s ORDER BY name ASC", [acctype, like_str]) - #FIXME: sanitize input like_str ^ + cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s ORDER BY name ASC", [acctype]) for inventory in cur: accts += [ Account.load(self.db, id = inventory[0]) ] return accts - - def fix_inventory(self, item, amount): - rv = self.db.execute_and_fetch( - "SELECT public.fix_inventory(%s, %s, %s, %s, %s, %s)", - [item.id, item.currency.id, self.excess.id, self.deficit.id, self.currency.id, amount] - )[0] - - self.db.commit() - return rv - #amount_in_reality = amount - #amount_in_system = item.balance() - #(buy, sell) = item.currency.rates(self.currency) - - #diff = abs(amount_in_reality - amount_in_system) - #buy_total = buy * diff - #if amount_in_reality > amount_in_system: - # transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality)) - # item.debit(transaction, diff, "Inventory fix excess") - # self.excess.credit(transaction, buy_total, "Inventory fix excess " + item.name) - # self.db.commit() - # return True - #elif amount_in_reality < amount_in_system: - # transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality)) - # item.credit(transaction, diff, "Inventory fix deficit") - # self.deficit.debit(transaction, buy_total, "Inventory fix deficit " + item.name) - # self.db.commit() - # return True - #else: - # transaction = self._transaction(description = "BrmBar inventory fix of {}pcs {} in system to {}pcs in reality".format(amount_in_system, item.name,amount_in_reality)) - # item.debit(transaction, 0, "Inventory fix - amount was correct") - # item.credit(transaction, 0, "Inventory fix - amount was correct") - # self.db.commit() - # return False - - 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] - #transaction = self._transaction(description = "BrmBar inventory consolidation") - #excess_balance = self.excess.balance() - #if excess_balance != 0: - # print("Excess balance {} debited to profit".format(-excess_balance)) - # self.excess.debit(transaction, -excess_balance, "Excess balance added to profit.") - # self.profits.debit(transaction, -excess_balance, "Excess balance added to profit.") - #deficit_balance = self.deficit.balance() - #if deficit_balance != 0: - # print("Deficit balance {} credited to profit".format(deficit_balance)) - # self.deficit.credit(transaction, deficit_balance, "Deficit balance removed from profit.") - # self.profits.credit(transaction, deficit_balance, "Deficit balance removed from profit.") - if msg != None: - print(msg) - self.db.commit() - - def undo(self, oldtid): - #description = self.db.execute_and_fetch("SELECT description FROM transactions WHERE id = %s", [oldtid])[0] - #description = 'undo %d (%s)' % (oldtid, description) - - #transaction = self._transaction(description=description) - #for split in self.db.execute_and_fetchall("SELECT id, side, account, amount, memo FROM transaction_splits WHERE transaction = %s", [oldtid]): - # splitid, side, account, amount, memo = split - # memo = 'undo %d (%s)' % (splitid, memo) - # amount = -amount - # self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, account, amount, memo]) - transaction = self.db.execute_and_fetch("SELECT public.undo_transaction(%s)",[oldtid])[0] - 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/daily-summary.sh b/brmbar3/daily-summary.sh deleted file mode 100755 index 2fe58bd..0000000 --- a/brmbar3/daily-summary.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# Add a crontab entry like: -# 5 4 * * * ~/brmbar/brmbar3/daily-summary.sh | mail -s "daily brmbar summary" rada@brmlab.cz -cd ~/brmbar/brmbar3 -./brmbar-cli.py stats -echo -echo "Time since last full inventory check: $(echo "select now()-time from transactions where description = 'BrmBar inventory consolidation' order by time desc limit 1;" | psql brmbar | tail -n +3 | head -n 1 | tr -s " ")" -echo -echo "Overflows: $(echo "SELECT name, -crbalance FROM account_balances WHERE name LIKE '%overflow%' AND crbalance != 0 ORDER BY name" | psql brmbar | tail -n +3 | grep '|' | tr -s " " | sed -e "s/ |/:/g" -e "s/$/;/" | tr -d "\n") TOTAL: $(echo "SELECT -SUM(crbalance) FROM account_balances WHERE name LIKE '%overflow%' AND crbalance != 0" | psql brmbar | tail -n +3 | head -n 1 | tr -s " ")" -echo -echo "Club Mate sold in last 24 hours: $(echo "select count(*) from transaction_cashsums where time > now() - '1 day'::INTERVAL and (description like '%Club Mate%' or description like '%granatove mate%')" | psql brmbar | tail -n +3 | head -n 1 | tr -s " ") bottles" 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/doc/architecture.md b/brmbar3/doc/architecture.md deleted file mode 100644 index 791517f..0000000 --- a/brmbar3/doc/architecture.md +++ /dev/null @@ -1,106 +0,0 @@ -BrmBar v3 - Architectural Overview -================================== - -BrmBar v3 is written in Python, with the database stored in PostgreSQL -and the primary user interface modelled in QtQuick. All user interfaces -share a common *brmbar* package that provides few Python classes for -manipulation with the base objects. - -Objects and Database Schema ---------------------------- - -### Account ### - -The most essential brmbar object is an Account, which can track -balances of various kinds (described by *acctype* column) in the -classical accounting paradigm: - -* **Cash**: A physical stash of cash. One cash account is created - by default, corresponding to the cash box where people put money - when buying stuff (or depositing money in their user accounts). - Often, that's the only cash account you need. -* **Debt**: Represents brmbar's debt to some person. These accounts - are actually the "user accounts" where people deposit money. When - a deposit of 100 is made, 100 is *subtracted* from the balance, - the balance is -100 and brmbar is in debt of 100 to the user. - When the user buys something for 200, 200 is *added* to the balance, - the balance is 100 and the user is in debt of 100 to the brmbar. - This is correct notation from accounting point of view, but somewhat - confusing for the users, so in the user interface (and crbalance - column of some views), this balance is *negated*! -* **Inventory**: Represents inventory items (e.g. Club Mate bottles). - The account balance represents the quantity of items. -* **Income**: Represents pure income of brmbar, i.e. the profit; - there is usually just a single account of this type where all the - profit (sell price of an item minus the buy price of an item) - is accumulated. -* **Expense**: This type is currently not used. -* **Starting balance** and **ending balance**: This may be used - in the future when transaction book needs to be compressed. - -As you can see, the amount of cash, user accounts, inventory items -etc. are all represented as **Account** objects that are of various -**types**, are **named** and have a certain balance (calculated -from a transaction book). That balance is a number represented -in certain **currency**. It also has a set of **barcodes** associated. - -### Currency, Exchange rate ### - -Usually, all accounts that deal with cash (the cash, debt, income, ... -accounts) share a single currency that corresponds to the physical -currency locally in use (the default is `Kč`). However, inventory -items have balances corresponding to item quantities - to deal with -this correctly, each inventory item *has its own currency*; i.e. -`Club Mate` bottle is a currency associated with the `Club Mate` -account. - -Currencies have defined (uni-directional) exchange rates. The exchange -rate of "Kč to Club Mate bottles" is the buy price of Club Mate, how -much you pay for one bottle of Club Mate from the cash box when you -are stocking in Club Mate. The exchange rate of "Club Mate bottle to Kč" -is the sell price of Club Mate, how much you pay for one bottle of Club -Mate to the cash box when you are buying it from brmbar (sell price -should be higher than buy price if you want to make a profit). - -Exchange rate is valid since some defined time; historical exchange -rates are therefore kept and this allows to account for changing prices -of inventory items. (Unfortunately, at the time of writing this, the -profit calculation actually didn't make use of that yet.) - -### Transactions, Transaction splits ### - -A transaction book is used to determine current account balances and -stores all operations related to accounts - depositing or withdrawing -money, stocking in items, and most importantly buying stuff (either for -cash or from a debt account). A transaction happenned at some **time** -and was performed by certain **responsible** person. - -The actual accounts involved in a transaction are specified by a list of -transaction splits that either put balance into the transaction (*credit* -side) or grab balance from it (*debit* side). For example, a typical -transaction representing a sale of Club Mate bottle to user "pasky" -would be split like this: - -* *credit* of 1 Club Mate on Club Mate account with memo "pasky". -* *debit* of 35 Kč on "pasky" account with memo "Club Mate" - (indeed we _add_ 35Kč to the debt account for pasky buying - the Club Mate; if this seems weird, refer to the "debt" account - type description). -* *debit* of 5 Kč on income account Profits with memo "Margin - on Club Mate" (this represents the sale price - buy price delta, - i.e. the profit we made in brmbar by selling this Club Mate). - -The brmbar Python Package -------------------------- - -The **brmbar** package (in brmbar/ subdirectory) provides common brmbar -functionality for the various user interfaces: - -* **Database**: Layer for performing SQL queries with some error handling. -* **Currency**: Class for querying and manipulating currency objects and - converting between them based on stored exchange rates. -* **Account**: Class for querying and manipulating the account objects - and their current balance. -* **Shop**: Class providing the "business logic" of all the actual user - operations: selling stuff, depositing and withdrawing moeny, adding - stock, retrieving list of accounts of given type, etc. 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 811a5f5..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 INTO STRICT v_rate, rate_dir INTO STRICT v_rate_dir FROM public.exchange_rates WHERE target = i_item_id AND source = i_other_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1; - IF v_rate_dir = 'target_to_source'::public.exchange_rate_direction THEN - RETURN v_rate; - ELSE - RETURN 1/v_rate; - END IF; -EXCEPTION - WHEN NO_DATA_FOUND THEN - RETURN -1; -END; -$$ - - --- return negative number on rate not found -CREATE OR REPLACE FUNCTION public.find_sell_rate( - IN i_item_id public.accounts.id%TYPE; - IN i_other_id public.accounts.id%TYPE; -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -DECLARE - v_rate public.exchange_rates.rate%TYPE; - v_rate_dir public.exchange_rates.rate_dir%TYPE; -BEGIN - SELECT rate INTO STRICT v_rate, rate_dir INTO STRICT v_rate_dir FROM public.exchange_rates WHERE target = i_other_id AND source = i_item_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1; - IF v_rate_dir = 'source_to_target'::public.exchange_rate_direction THEN - RETURN v_rate; - ELSE - RETURN 1/v_rate; - END IF; -EXCEPTION - WHEN NO_DATA_FOUND THEN - RETURN -1; -END; -$$ - -CREATE OR REPLACE FUNCTION public.create_transaction( - i_responsible_id public.accounts.id%TYPE, - i_description public.transactions.description%TYPE -) RETURNS public.transactions.id%TYPE AS $$ -DECLARE - new_transaction_id public.transactions%TYPE; -BEGIN - -- Create a new transaction - INSERT INTO public.transactions (responsible, description) - VALUES (i_responsible_id, i_description) - RETURNING id INTO new_transaction_id; - -- Return the new transaction ID - RETURN new_transaction_id; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION public.sell_item( - i_item_id public.accounts.id%TYPE, - i_amount INTEGER, - i_user_id public.accounts.id%TYPE, - i_target_currency_id public.currencies.id%TYPE, - i_description TEXT -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -DECLARE - v_buy_rate NUMERIC; - v_sell_rate NUMERIC; - v_cost NUMERIC; - v_profit NUMERIC; - v_transaction_id public.transactions.id%TYPE; -BEGIN - -- Get the buy and sell rates from the stored functions - v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id); - v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id); - - -- Calculate cost and profit - v_cost := i_amount * v_sell_rate; - v_profit := i_amount * (v_sell_rate - v_buy_rate); - - -- Create a new transaction - v_transaction_id := public.create_transaction(i_user_id, i_description); - - -- the item (decrease stock) - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'credit', i_item_id, i_amount, - (SELECT "name" FROM public.accounts WHERE id = i_user_id)); - - -- the user - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'debit', i_user_id, v_cost, - (SELECT "name" FROM public.accounts WHERE id = i_item_id)); - - -- the profit - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'debit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id)); - - -- Return the cost - RETURN v_cost; -END; -$$; - -PERFORM brmbar_privileged.upgrade_schema_version_to(9); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : 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 23ad131..0000000 --- a/brmbar3/schema/0010-shop-sell-for-cash.sql +++ /dev/null @@ -1,143 +0,0 @@ --- --- 0010-shop-sell-for-cash.sql --- --- #10 - stored function for cash sell transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- Permission to use, copy, modify, and/or distribute this software --- for any purpose with or without fee is hereby granted, provided --- that the above copyright notice and this permission notice appear --- in all copies. --- --- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL --- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE --- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR --- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS --- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, --- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN --- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --- - --- To require fully-qualified names -SELECT pg_catalog.set_config('search_path', '', false); - -DO $upgrade_block$ -BEGIN - -IF brmbar_privileged.has_exact_schema_version(9) THEN - -CREATE OR REPLACE FUNCTION brmbar_privileged.create_transaction( - i_responsible_id public.accounts.id%TYPE, - i_description public.transactions.description%TYPE -) RETURNS public.transactions.id%TYPE AS $$ -DECLARE - new_transaction_id public.transactions%TYPE; -BEGIN - -- Create a new transaction - INSERT INTO public.transactions (responsible, description) - VALUES (i_responsible_id, i_description) - RETURNING id INTO new_transaction_id; - -- Return the new transaction ID - RETURN new_transaction_id; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION brmbar_privileged.sell_item_internal( - i_item_id public.accounts.id%TYPE, - i_amount INTEGER, - i_user_id public.accounts.id%TYPE, - i_target_currency_id public.currencies.id%TYPE, - i_other_memo TEXT, - i_description TEXT -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -DECLARE - v_buy_rate NUMERIC; - v_sell_rate NUMERIC; - v_cost NUMERIC; - v_profit NUMERIC; - v_transaction_id public.transactions.id%TYPE; -BEGIN - -- Get the buy and sell rates from the stored functions - v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id); - v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id); - - -- Calculate cost and profit - v_cost := i_amount * v_sell_rate; - v_profit := i_amount * (v_sell_rate - v_buy_rate); - - -- Create a new transaction - v_transaction_id := brmbar_privileged.create_transaction(i_user_id, i_description); - - -- the item (decrease stock) - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'credit', i_item_id, i_amount, - i_other_memo); - - -- the user - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'debit', i_user_id, v_cost, - (SELECT "name" FROM public.accounts WHERE id = i_item_id)); - - -- the profit - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'debit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id)); - - -- Return the cost - RETURN v_cost; -END; -$$; - -CREATE OR REPLACE FUNCTION public.sell_item( - i_item_id public.accounts.id%TYPE, - i_amount INTEGER, - i_user_id public.accounts.id%TYPE, - i_target_currency_id public.currencies.id%TYPE, - i_description TEXT -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN brmbar_privileged.sell_item_internal(i_item_id, - i_amount, - i_other_id, - i_target_currency_id, - (SELECT "name" FROM public.accounts WHERE id = i_user_id), - i_description); -END; -$$; - -CREATE OR REPLACE FUNCTION public.sell_item_for_cash( - i_item_id public.accounts.id%TYPE, - i_amount INTEGER, - i_user_id public.accounts.id%TYPE, - i_target_currency_id public.currencies.id%TYPE, - i_description TEXT -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN brmbar_privileged.sell_item_internal(i_item_id, - i_amount, - i_other_id, - i_target_currency_id, - 'Cash', - i_description); -END; -$$; - -DROP FUNCTION public.create_transaction; - -PERFORM brmbar_privileged.upgrade_schema_version_to(10); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/schema/0011-shop-undo-sale.sql b/brmbar3/schema/0011-shop-undo-sale.sql deleted file mode 100644 index 67d47cc..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%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 5cf12bb..0000000 --- a/brmbar3/schema/0015-shop-buy-for-cash.sql +++ /dev/null @@ -1,82 +0,0 @@ --- --- 0015-shop-buy-for-cash.sql --- --- #15 - stored function for cash-based stock replenishment transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- Permission to use, copy, modify, and/or distribute this software --- for any purpose with or without fee is hereby granted, provided --- that the above copyright notice and this permission notice appear --- in all copies. --- --- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL --- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE --- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR --- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS --- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, --- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN --- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --- - --- To require fully-qualified names -SELECT pg_catalog.set_config('search_path', '', false); - -DO $upgrade_block$ -BEGIN - -IF brmbar_privileged.has_exact_schema_version(14) THEN - -CREATE OR REPLACE FUNCTION public.buy_for_cash( - i_cash_account_id public.accounts.id%TYPE, - i_item_id public.accounts.id%TYPE, - i_amount INTEGER, - i_target_currency_id public.currencies.id%TYPE, - i_item_name TEXT -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -DECLARE - v_buy_rate NUMERIC; - v_cost NUMERIC; - v_transaction_id public.transactions.id%TYPE; -BEGIN - -- this could fail and it would generate exception in python - -- FIXME: convert v_buy_rate < 0 into python exception - v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id); - -- this could fail and it would generate exception in python, even though it is not used - --v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id); - - -- Calculate cost and profit - v_cost := i_amount * v_buy_rate; - - -- Create a new transaction - v_transaction_id := brmbar_privileged.create_transaction(NULL, - 'BrmBar stock replenishment of ' || i_amount || 'x ' || i_item_name || ' for cash'); - - -- the item - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'debit', i_item_id, i_amount, - 'Cash'); - - -- the cash - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'credit', i_cash_account_id, v_cost, - i_item_name); - - -- Return the cost - RETURN v_cost; -END; -$$; - -PERFORM brmbar_privileged.upgrade_schema_version_to(15); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : 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 21af8b1..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 (i_transaction_id, 'credit', i_profits_id, i_amount, i_user_name); - -- the user - INSERT INTO public.transaction_splits (transaction, side, account, amount, memo) - VALUES (i_transaction_id, 'credit', i_user_id, i_amount, 'Credit from receipt: ' || i_description); -END; -$$; - -PERFORM brmbar_privileged.upgrade_schema_version_to(16); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/schema/0017-shop-fix-inventory.sql b/brmbar3/schema/0017-shop-fix-inventory.sql deleted file mode 100644 index 3bacd8d..0000000 --- a/brmbar3/schema/0017-shop-fix-inventory.sql +++ /dev/null @@ -1,157 +0,0 @@ --- --- 0017-shop-fix-inventory.sql --- --- #17 - stored function for "fixing" inventory transaction --- --- ISC License --- --- Copyright 2023-2025 Brmlab, z.s. --- TMA --- --- Permission to use, copy, modify, and/or distribute this software --- for any purpose with or without fee is hereby granted, provided --- that the above copyright notice and this permission notice appear --- in all copies. --- --- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL --- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED --- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE --- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR --- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS --- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, --- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN --- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --- - --- To require fully-qualified names -SELECT pg_catalog.set_config('search_path', '', false); - -DO $upgrade_block$ -BEGIN - -IF brmbar_privileged.has_exact_schema_version(16) THEN - -CREATE OR REPLACE FUNCTION public.compute_account_balance( - i_account_id public.accounts.id%TYPE -) RETURNS NUMERIC -LANGUAGE plpgsql -AS $$ -DECLARE - v_crsum NUMERIC; - v_dbsum NUMERIC; -BEGIN - SELECT COALESCE(SUM(CASE WHEN side='credit' THEN amount ELSE 0 END),0) crsum INTO v_crsum, - COALESCE(SUM(CASE WHEN side='debit' THEN amount ELSE 0 END),0) dbsum into v_dbsum - FROM public.transaction_splits ts WHERE ts.account=4 - RETURN v_dbsum - v_crsum; -END; $$; - -CREATE OR REPLACE FUNCTION brmbar_privileged.fix_account_balance( - IN i_account_id public.acounts.id%TYPE, - IN i_account_currency_id public.currencies.id%TYPE, - IN i_excess_id public.acounts.id%TYPE, - IN i_deficit_id public.acounts.id%TYPE, - IN i_shop_currency_id public.currencies.id%TYPE, - IN i_amount_in_reality NUMERIC -) RETURNS BOOLEAN -VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$ -DECLARE - v_amount_in_system NUMERIC; - v_buy_rate NUMERIC; - v_currency_id public.currencies.id%TYPE; - v_diff NUMERIC; - v_buy_total NUMERIC; - v_ntrn_id public.transactions.id%TYPE; - v_transaction_memo TEXT; - v_item_name TEXT; - v_excess_memo TEXT; - v_deficit_memo TEXT; - - v_old_trn public.transactions%ROWTYPE; - v_old_split public.transaction_splits%ROWTYPE; -BEGIN - v_amount_in_system := public.compute_account_balance(i_account_id); - IF i_account_currency_id <> i_shop_currency_id THEN - v_buy_rate := public.find_buy_rate(i_item_id, i_shop_currency_id); - ELSE - v_buy_rate := 1; - END IF; - - v_diff := ABS(i_amount_in_reality - v_amount_in_system); - v_buy_total := v_buy_rate * v_diff; - -- compute memo strings - IF i_item_id = 1 THEN -- cash account recognized by magic id - -- fixing cash - v_transaction_memo := - 'BrmBar cash inventory fix of ' || v_amount_in_system - || ' in system to ' || i_amount_in_reality || ' in reality'; - v_excess_memo := 'Inventory cash fix excess.'; - v_deficit_memo := 'Inventory fix deficit.'; - ELSE - -- fixing other account - SELECT "name" INTO v_item_name FROM public.accounts WHERE id = i_account_id; - v_transaction_memo := - 'BrmBar inventory fix of ' || v_amount_in_system || 'pcs ' - || v_item_name - || ' in system to ' || i_amount_in_reality || 'pcs in reality'; - v_excess_memo := 'Inventory fix excess ' || v_item_name; - v_deficit_memo := 'Inventory fix deficit ' || v_item_name; - END IF; - -- create transaction based on the relation between counting and accounting - IF i_amount_in_reality > v_amount_in_system THEN - v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'debit', i_item_id, v_diff, 'Inventory fix excess'); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'credit', i_excess_id, v_buy_total, v_excess_memo); - RETURN TRUE; - ELSIF i_amount_in_reality < v_amount_in_system THEN - v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'credit', i_item_id, v_diff, 'Inventory fix deficit'); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'debit', i_deficit_id, v_buy_total, v_deficit_memo); - RETURN TRUE; - ELSIF i_account_id <> 1 THEN -- cash account recognized by magic id - -- record that everything is going on swimmingly only for noncash accounts (WTF) - v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'debit', i_item_id, 0, 'Inventory fix - amount was correct'); - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, 'credit', i_item_id, 0, 'Inventory fix - amount was correct'); - RETURN FALSE; - END IF; - RETURN FALSE; -END; -$fn$; - - -CREATE OR REPLACE FUNCTION public.fix_inventory( - IN i_account_id public.acounts.id%TYPE, - IN i_account_currency_id public.currencies.id%TYPE, - IN i_excess_id public.acounts.id%TYPE, - IN i_deficit_id public.acounts.id%TYPE, - IN i_shop_currency_id public.currencies.id%TYPE, - IN i_amount_in_reality NUMERIC -) RETURNS BOOLEAN -VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$ -BEGIN - RETURN brmbar_privileged.fix_account_balance( - i_account_id, - i_account_currency_id, - i_excess_id, - i_deficit_id, - i_shop_currency_id, - i_amount_in_reality - ); -END; -$fn$; - - -PERFORM brmbar_privileged.upgrade_schema_version_to(17); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : diff --git a/brmbar3/schema/0018-shop-fix-cash.sql b/brmbar3/schema/0018-shop-fix-cash.sql deleted file mode 100644 index 2af5d72..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.acounts.id%TYPE, - IN i_deficit_id public.acounts.id%TYPE, - IN i_shop_currency_id public.currencies.id%TYPE, - IN i_amount_in_reality NUMERIC -) RETURNS BOOLEAN -VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$ -BEGIN - RETURN brmbar_privileged.fix_account_balance( - 1, - 1, - i_excess_id, - i_deficit_id, - i_shop_currency_id, - i_amount_in_reality - ); -END; -$fn$; - - -PERFORM brmbar_privileged.upgrade_schema_version_to(18); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : 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 a279580..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 '||o_id||' ('||v_old_trn.description||')') RETURNING id into v_ntrn_id; - FOR v_old_split IN - SELECT * FROM transaction_splits WHERE "transaction" = i_id - LOOP - INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo") - VALUES (v_ntrn_id, v_old_split.side, v_old_split.account, -v_old_split.amount, - 'undo ' || v_old_split.id || ' (' || v_old_split.memo || ')' ); - END LOOP; - RETURN v_ntrn_id; -END; -$fn$; - - - -PERFORM brmbar_privileged.upgrade_schema_version_to(20); -END IF; - -END; -$upgrade_block$; - --- vim: set ft=plsql : 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 -