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 a2dc10b..0000000
--- a/brmbar3/brmbar-tui.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/python3
-
-import sys
-
-from brmbar import Database
-
-import brmbar
-
-print("!!! THIS PROGRAM NO LONGER WORKS !!!")
-sys.exit(1)
-
-db = Database.Database("dbname=brmbar")
-shop = brmbar.Shop.new_with_defaults(db)
-currency = shop.currency
-
-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 522e9ed..0000000
--- a/brmbar3/brmbar-web.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/python
-
-import sys
-
-from brmbar import Database
-
-import brmbar
-
-print("!!! THIS PROGRAM NO LONGER WORKS !!!")
-sys.exit(1)
-
-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 = '| Id | Item Name | Bal. |
'
- 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 += '| %d | %s | %d |
' % (style, a.id, a.name, balance)
- response += '
'
- 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 2499d05..29dd79e 100644
--- a/brmbar3/brmbar/Account.py
+++ b/brmbar3/brmbar/Account.py
@@ -1,15 +1,10 @@
from .Currency import Currency
-import logging
-
-logger = logging.getLogger(__name__)
-
class Account:
- """BrmBar Account
+ """ BrmBar Account
Both users and items are accounts. So is the money box, etc.
Each account has a currency."""
-
def __init__(self, db, id, name, currency, acctype):
self.db = db
self.id = id
@@ -19,35 +14,42 @@ class Account:
@classmethod
def load_by_barcode(cls, db, barcode):
- logger.debug("load_by_barcode: '%s'", barcode)
- account_id, account_name, account_acctype, currency_id, currency_name = db.execute_and_fetch(
- "SELECT account_id, account_name, account_acctype, currency_id, currency_name FROM public.account_class_initialization_data('by_barcode', NULL, %s)",
- [barcode])
- currency = Currency(db, currency_id, currency_name)
- return cls(db, account_id, account_name, currency, account_acctype)
+ res = db.execute_and_fetch("SELECT account FROM barcodes WHERE barcode = %s", [barcode])
+ if res is None:
+ return None
+ id = res[0]
+ return cls.load(db, id = id)
@classmethod
- def load(cls, db, id=None):
- """Constructor for existing account"""
- account_id, account_name, account_acctype, currency_id, currency_name = db.execute_and_fetch(
- "SELECT account_id, account_name, account_acctype, currency_id, currency_name FROM public.account_class_initialization_data('by_id', %s, NULL)",
- [id])
- currency = Currency(db, currency_id, currency_name)
- return cls(db, account_id, account_name, currency, account_acctype)
+ def load(cls, db, id = None, name = None):
+ """ Constructor for existing account """
+ if id is not None:
+ name = db.execute_and_fetch("SELECT name FROM accounts WHERE id = %s", [id])
+ name = name[0]
+ elif name is not None:
+ id = db.execute_and_fetch("SELECT id FROM accounts WHERE name = %s", [name])
+ id = id[0]
+ else:
+ raise NameError("Account.load(): Specify either id or name")
+
+ currid, acctype = db.execute_and_fetch("SELECT currency, acctype FROM accounts WHERE id = %s", [id])
+ currency = Currency.load(db, id = currid)
+
+ return cls(db, name = name, id = id, currency = currency, acctype = acctype)
@classmethod
def create(cls, db, name, currency, acctype):
- """Constructor for new account"""
- id = db.execute_and_fetch(
- "SELECT public.create_account(%s, %s, %s)", [name, currency.id, acctype]
- )
- return cls(db, name=name, id=id, currency=currency, acctype=acctype)
+ """ Constructor for new account """
+ id = db.execute_and_fetch("INSERT INTO accounts (name, currency, acctype) VALUES (%s, %s, %s) RETURNING id", [name, currency.id, acctype])
+ id = id[0]
+ return cls(db, name = name, id = id, currency = currency, acctype = acctype)
def balance(self):
- bal = self.db.execute_and_fetch(
- "SELECT public.compute_account_balance(%s)", [self.id]
- )[0]
- return bal
+ 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())
@@ -55,12 +57,20 @@ class Account:
def negbalance_str(self):
return self.currency.str(-self.balance())
+ def debit(self, transaction, amount, memo):
+ return self._transaction_split(transaction, 'debit', amount, memo)
+
+ def credit(self, transaction, amount, memo):
+ return self._transaction_split(transaction, 'credit', amount, memo)
+
+ def _transaction_split(self, transaction, side, amount, memo):
+ """ Common part of credit() and debit(). """
+ self.db.execute("INSERT INTO transaction_splits (transaction, side, account, amount, memo) VALUES (%s, %s, %s, %s, %s)", [transaction, side, self.id, amount, memo])
+
def add_barcode(self, barcode):
- self.db.execute(
- "SELECT public.add_barcode_to_account(%s, %s)", [self.id, barcode]
- )
+ self.db.execute("INSERT INTO barcodes (account, barcode) VALUES (%s, %s)", [self.id, barcode])
self.db.commit()
def rename(self, name):
- self.db.execute("SELECT public.rename_account(%s, %s)", [self.id, name])
+ self.db.execute("UPDATE accounts SET name = %s WHERE id = %s", [name, self.id])
self.name = name
diff --git a/brmbar3/brmbar/Currency.py b/brmbar3/brmbar/Currency.py
index fba3059..b382d77 100644
--- a/brmbar3/brmbar/Currency.py
+++ b/brmbar3/brmbar/Currency.py
@@ -1,12 +1,9 @@
-# vim: set fileencoding=utf8
-
class Currency:
- """Currency
-
+ """ Currency
+
Each account has a currency (1 Kč, 1 Club Maté, ...), pairs of
- currencies have (asymmetric) exchange rates."""
-
+ currencies have (asymmetric) exchange rates. """
def __init__(self, db, id, name):
self.db = db
self.id = id
@@ -14,70 +11,63 @@ class Currency:
@classmethod
def default(cls, db):
- """Default wallet currency"""
- return cls.load(db, name="Kč")
+ """ Default wallet currency """
+ return cls.load(db, name = "Kč")
@classmethod
- def load(cls, db, id=None):
- """Constructor for existing currency"""
- if id is None:
- raise NameError("Currency.load(): Specify id")
- name = db.execute_and_fetch("SELECT name FROM currencies WHERE id = %s", [id])
- name = name[0]
- return cls(db, id=id, name=name)
+ def load(cls, db, id = None, name = None):
+ """ Constructor for existing currency """
+ if id is not None:
+ name = db.execute_and_fetch("SELECT name FROM currencies WHERE id = %s", [id])
+ name = name[0]
+ elif name is not None:
+ id = db.execute_and_fetch("SELECT id FROM currencies WHERE name = %s", [name])
+ id = id[0]
+ else:
+ raise NameError("Currency.load(): Specify either id or name")
+ return cls(db, name = name, id = id)
@classmethod
def create(cls, db, name):
- """Constructor for new currency"""
- id = db.execute_and_fetch("SELECT public.create_currency(%s)", [name])
- return cls(db, id=id, name=name)
+ """ Constructor for new currency """
+ id = db.execute_and_fetch("INSERT INTO currencies (name) VALUES (%s) RETURNING id", [name])
+ id = id[0]
+ return cls(db, name = name, id = id)
def rates(self, other):
- """Return tuple ($buy, $sell) of rates of $self in relation to $other (brmbar.Currency):
+ """ Return tuple ($buy, $sell) of rates of $self in relation to $other (brmbar.Currency):
$buy is the price of $self in means of $other when buying it (into brmbar)
- $sell is the price of $self in means of $other when selling it (from brmbar)"""
- # buy rate
- res = self.db.execute_and_fetch(
- "SELECT public.find_buy_rate(%s, %s)", [self.id, other.id]
- )
+ $sell is the price of $self in means of $other when selling it (from brmbar) """
+
+ res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [self.id, other.id])
if res is None:
- raise NameError("Something fishy in find_buy_rate.")
- buy = res[0]
- if buy < 0:
- raise NameError(
- "Currency.rate(): Unknown conversion "
- + other.name()
- + " to "
- + self.name()
- )
- # sell rate
- res = self.db.execute_and_fetch(
- "SELECT public.find_sell_rate(%s, %s)", [self.id, other.id]
- )
+ raise NameError("Currency.rate(): Unknown conversion " + other.name() + " to " + self.name())
+ buy_rate, buy_rate_dir = res
+ buy = buy_rate if buy_rate_dir == "target_to_source" else 1/buy_rate
+
+ res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [other.id, self.id])
if res is None:
- raise NameError("Something fishy in find_sell_rate.")
- sell = res[0]
- if sell < 0:
- raise NameError(
- "Currency.rate(): Unknown conversion "
- + self.name()
- + " to "
- + other.name()
- )
+ raise NameError("Currency.rate(): Unknown conversion " + self.name() + " to " + other.name())
+ sell_rate, sell_rate_dir = res
+ sell = sell_rate if sell_rate_dir == "source_to_target" else 1/sell_rate
return (buy, sell)
+ def convert(self, amount, target):
+ res = self.db.execute_and_fetch("SELECT rate, rate_dir FROM exchange_rates WHERE target = %s AND source = %s AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1", [target.id, self.id])
+ if res is None:
+ raise NameError("Currency.convert(): Unknown conversion " + self.name() + " to " + target.name())
+ rate, rate_dir = res
+ if rate_dir == "source_to_target":
+ resamount = amount * rate
+ else:
+ resamount = amount / rate
+ return resamount
+
def str(self, amount):
return "{:.2f} {}".format(amount, self.name)
def update_sell_rate(self, target, rate):
- self.db.execute(
- "SELECT public.update_currency_sell_rate(%s, %s, %s)",
- [self.id, target.id, rate],
- )
-
+ self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [self.id, target.id, rate, "source_to_target"])
def update_buy_rate(self, source, rate):
- self.db.execute(
- "SELECT public.update_currency_buy_rate(%s, %s, %s)",
- [self.id, source.id, rate],
- )
+ self.db.execute("INSERT INTO exchange_rates (source, target, rate, rate_dir) VALUES (%s, %s, %s, %s)", [source.id, self.id, rate, "target_to_source"])
diff --git a/brmbar3/brmbar/Database.py b/brmbar3/brmbar/Database.py
index 5b844dd..e5962ab 100644
--- a/brmbar3/brmbar/Database.py
+++ b/brmbar3/brmbar/Database.py
@@ -3,30 +3,25 @@
import psycopg2
from contextlib import closing
import time
-import logging
-
-logger = logging.getLogger(__name__)
-
class Database:
"""self-reconnecting database object"""
-
def __init__(self, dsn):
self.db_conn = psycopg2.connect(dsn)
self.dsn = dsn
- def execute(self, query, attrs=None):
+ def execute(self, query, attrs = None):
"""execute a query and return one result"""
with closing(self.db_conn.cursor()) as cur:
cur = self._execute(cur, query, attrs)
- def execute_and_fetch(self, query, attrs=None):
+ def execute_and_fetch(self, query, attrs = None):
"""execute a query and return one result"""
with closing(self.db_conn.cursor()) as cur:
cur = self._execute(cur, query, attrs)
return cur.fetchone()
- def execute_and_fetchall(self, query, attrs=None):
+ def execute_and_fetchall(self, query, attrs = None):
"""execute a query and return all results"""
with closing(self.db_conn.cursor()) as cur:
cur = self._execute(cur, query, attrs)
@@ -35,39 +30,25 @@ class Database:
def _execute(self, cur, query, attrs, level=1):
"""execute a query, and in case of OperationalError (db restart)
reconnect to database. Recurses with increasig pause between tries"""
- logger.debug("SQL: (%s) @%s" % (query, time.strftime("%Y%m%d %a %I:%m %p")))
try:
if attrs is None:
cur.execute(query)
else:
cur.execute(query, attrs)
return cur
- except (
- psycopg2.DataError
- ) as error: # when biitr comes and enters '99999999999999999999' for amount
- logger.debug(
- "We have invalid input data (SQLi?): level %s (%s) @%s"
- % (level, error, time.strftime("%Y%m%d %a %I:%m %p"))
- )
- self.db_conn.rollback()
- raise RuntimeError("Unsanitized data entered again... BOBBY TABLES")
except psycopg2.OperationalError as error:
- logger.debug(
- "Sleeping: level %s (%s) @%s"
- % (level, error, time.strftime("%Y%m%d %a %I:%m %p"))
- )
- # TODO: emit message "db conn failed, reconnecting
- time.sleep(2**level)
+ print("Sleeping: level %s (%s) @%s" % (
+ level, error, time.strftime("%Y%m%d %a %I:%m %p")
+ ))
+ #TODO: emit message "db conn failed, reconnecting
+ time.sleep(2 ** level)
try:
self.db_conn = psycopg2.connect(self.dsn)
except psycopg2.OperationalError:
- # TODO: emit message "psql not running to interface
+ #TODO: emit message "psql not running to interface
time.sleep(1)
- cur = self.db_conn.cursor() # how ugly is this?
- return self._execute(cur, query, attrs, level + 1)
- except Exception as ex:
- logger.debug("_execute exception: %s", ex)
- self.db_conn.rollback()
+ cur = self.db_conn.cursor() #how ugly is this?
+ return self._execute(cur, query, attrs, level+1)
def commit(self):
"""passes commit to db"""
diff --git a/brmbar3/brmbar/Shop.py b/brmbar3/brmbar/Shop.py
index a2ef1d1..1623cac 100644
--- a/brmbar3/brmbar/Shop.py
+++ b/brmbar3/brmbar/Shop.py
@@ -1,168 +1,90 @@
import brmbar
from .Currency import Currency
from .Account import Account
-import logging
-
-logger = logging.getLogger(__name__)
-
class Shop:
- """BrmBar Shop
+ """ BrmBar Shop
Business logic so that only interaction is left in the hands
- of the frontend scripts."""
-
- def __init__(self, db, currency_id, profits_id, cash_id, excess_id, deficit_id):
- # Keep db as-is
+ of the frontend scripts. """
+ def __init__(self, db, currency, profits, cash):
self.db = db
-
- # Store all ids
- self.currency_id = currency_id
- self.profits_id = profits_id
- self.cash_id = cash_id
- self.excess_id = excess_id
- self.deficit_id = deficit_id
-
- # Create objects where needed for legacy code
-
- # brmbar.Currency
- self.currency = Currency.load(self.db, id=self.currency_id)
-
- # income brmbar.Account for brmbar profit margins on items
- self.profits = Account.load(db, id=self.profits_id)
-
- # our operational ("wallet") cash account
- self.cash = Account.load(db, id=self.cash_id)
-
- # account from which is deducted cash during inventory item
- # fixing (when system contains less items than is the
- # reality)
- self.excess = Account.load(db, id=self.excess_id)
-
- # account where is put cash during inventory item fixing (when
- # system contains more items than is the reality)
- self.deficit = Account.load(db, id=self.deficit_id)
+ self.currency = currency # brmbar.Currency
+ self.profits = profits # income brmbar.Account for brmbar profit margins on items
+ self.cash = cash # our operational ("wallet") cash account
@classmethod
def new_with_defaults(cls, db):
- # shop_class_initialization_data
- currency_id, profits_id, cash_id, excess_id, deficit_id = db.execute_and_fetch(
- "select currency_id, profits_id, cash_id, excess_id, deficit_id from public.shop_class_initialization_data()"
- )
- return cls(
- db,
- currency_id=currency_id,
- profits_id=profits_id,
- cash_id=cash_id,
- excess_id=excess_id,
- deficit_id=deficit_id,
- )
+ return cls(db,
+ currency = Currency.default(db),
+ profits = Account.load(db, name = "BrmBar Profits"),
+ cash = Account.load(db, name = "BrmBar Cash"))
- def sell(self, item, user, amount=1):
- # Call the stored procedure for the sale
- logger.debug(
- "sell: item.id=%s amount=%s user.id=%s self.currency.id=%s",
- item.id,
- amount,
- user.id,
- self.currency.id,
- )
- res = self.db.execute_and_fetch(
- "SELECT public.sell_item(%s, %s, %s, %s, %s)",
- [
- item.id,
- amount,
- user.id,
- self.currency.id,
- "BrmBar sale of {0}x {1} to {2}".format(amount, item.name, user.name),
- ],
- )
- logger.debug("sell: res[0]=%s", res[0])
- cost = res[0]
+ def sell(self, item, user, amount = 1):
+ # Sale: Currency conversion from item currency to shop currency
+ (buy, sell) = item.currency.rates(self.currency)
+ cost = amount * sell
+ profit = amount * (sell - buy)
+
+ transaction = self._transaction(responsible = user, description = "BrmBar sale of {}x {} to {}".format(amount, item.name, user.name))
+ item.credit(transaction, amount, user.name)
+ user.debit(transaction, cost, item.name) # debit (increase) on a _debt_ account
+ self.profits.debit(transaction, profit, "Margin on " + item.name)
self.db.commit()
+
return cost
- def sell_for_cash(self, item, amount=1):
- cost = self.db.execute_and_fetch(
- "SELECT public.sell_item_for_cash(%s, %s, %s, %s, %s)",
- [
- item.id,
- amount,
- user.id,
- self.currency.id,
- "BrmBar sale of {0}x {1} for cash".format(amount, item.name),
- ],
- )[0]
+ def sell_for_cash(self, item, amount = 1):
+ # Sale: Currency conversion from item currency to shop currency
+ (buy, sell) = item.currency.rates(self.currency)
+ cost = amount * sell
+ profit = amount * (sell - buy)
+ transaction = self._transaction(description = "BrmBar sale of {}x {} for cash".format(amount, item.name))
+ item.credit(transaction, amount, "Cash")
+ self.cash.debit(transaction, cost, item.name)
+ self.profits.debit(transaction, profit, "Margin on " + item.name)
self.db.commit()
- return cost
- def undo_sale(self, item, user, amount=1):
- # Call the stored procedure for undoing a sale
- cost = self.db.execute_and_fetch(
- "SELECT public.undo_sale_of_item(%s, %s, %s, %s)",
- [
- item.id,
- amount,
- user.id,
- user.currency.id,
- "BrmBar sale UNDO of {0}x {1} to {2}".format(
- amount, item.name, user.name
- ),
- ],
- )[0]
- self.db.commit()
return cost
def add_credit(self, credit, user):
- self.db.execute_and_fetch(
- "SELECT public.add_credit(%s, %s, %s, %s)",
- [self.cash.id, credit, user.id, user.name],
- )
+ transaction = self._transaction(responsible = user, description = "BrmBar credit replenishment for " + user.name)
+ self.cash.debit(transaction, credit, user.name)
+ user.credit(transaction, credit, "Credit replenishment")
self.db.commit()
def withdraw_credit(self, credit, user):
- self.db.execute_and_fetch(
- "SELECT public.withdraw_credit(%s, %s, %s, %s)",
- [self.cash.id, credit, user.id, user.name],
- )
+ transaction = self._transaction(responsible = user, description = "BrmBar credit withdrawal for " + user.name)
+ self.cash.credit(transaction, credit, user.name)
+ user.debit(transaction, credit, "Credit withdrawal")
self.db.commit()
- def transfer_credit(self, userfrom, userto, amount):
- self.db.execute_and_fetch(
- "SELECT public.transfer_credit(%s, %s, %s, %s, %s, %s)",
- [self.cash.id, amount, userfrom.id, userfrom.name, userto.id, userto.name],
- )
- self.db.commit()
+ def buy_for_cash(self, item, amount = 1):
+ # Buy: Currency conversion from item currency to shop currency
+ (buy, sell) = item.currency.rates(self.currency)
+ cost = amount * buy
- def buy_for_cash(self, item, amount=1):
- iamount = int(amount)
- famount = float(iamount)
- assert famount == amount, "amount is not integer value %s".format(amount)
- cost = self.db.execute_and_fetch(
- "SELECT public.buy_for_cash(%s, %s, %s, %s, %s)",
- [self.cash.id, item.id, iamount, self.currency.id, item.name],
- )[0]
+ transaction = self._transaction(description = "BrmBar stock replenishment of {}x {} for cash".format(amount, item.name))
+ item.debit(transaction, amount, "Cash")
+ self.cash.credit(transaction, cost, item.name)
self.db.commit()
return cost
def receipt_to_credit(self, user, credit, description):
- self.db.execute_and_fetch(
- "SELECT public.receipt_reimbursement(%s, %s, %s, %s, %s)",
- [self.profits.id, user.id, user.name, credit, description],
- )[0]
+ transaction = self._transaction(responsible = user, description = "Receipt: " + description)
+ self.profits.credit(transaction, credit, user.name)
+ user.credit(transaction, credit, "Credit from receipt: " + description)
self.db.commit()
- def _transaction(self, responsible=None, description=None):
- transaction = self.db.execute_and_fetch(
- "INSERT INTO transactions (responsible, description) VALUES (%s, %s) RETURNING id",
- [responsible.id if responsible else None, description],
- )
+ def _transaction(self, responsible = None, description = None):
+ transaction = self.db.execute_and_fetch("INSERT INTO transactions (responsible, description) VALUES (%s, %s) RETURNING id",
+ [responsible.id if responsible else None, description])
transaction = transaction[0]
return transaction
- def credit_balance(self, overflow=None):
+ def credit_balance(self):
# We assume all debt accounts share a currency
sumselect = """
SELECT SUM(ts.amount)
@@ -170,80 +92,35 @@ 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"])
+ cur = self.db.execute_and_fetch(sumselect, ["debt", 'debit'])
debit = cur[0] or 0
- credit = self.db.execute_and_fetch(sumselect, ["debt", "credit"])
+ 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())
def inventory_balance(self):
- resa = self.db.execute_and_fetch("SELECT * FROM public.inventory_balance()")
- res = resa[0]
- logger.debug("inventory_balance resa = %s", resa)
- return res
-
+ balance = 0
+ # Each inventory account has its own currency,
+ # so we just do this ugly iteration
+ cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s", ["inventory"])
+ for inventory in cur:
+ invid = inventory[0]
+ inv = Account.load(self.db, id = invid)
+ # FIXME: This is not correct as each instance of inventory
+ # might have been bought for a different price! Therefore,
+ # we need to replace the command below with a complex SQL
+ # statement that will... ugh, accounting is hard!
+ balance += inv.currency.convert(inv.balance(), self.currency)
+ return balance
def inventory_balance_str(self):
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 a.id, a.name aname, a.currency, a.acctype, c.name cname FROM accounts a JOIN currencies c ON c.id=a.currency WHERE a.acctype = %s AND a.name ILIKE %s ORDER BY a.name ASC",
- [acctype, like_str],
- )
- # FIXME: sanitize input like_str ^
+ cur = self.db.execute_and_fetchall("SELECT id FROM accounts WHERE acctype = %s ORDER BY name ASC", [acctype])
for inventory in cur:
- curr = Currency(db=self.db, id=inventory[2], name=inventory[4]);
- accts += [Account(self.db, id=inventory[0], name=inventory[1], currency=curr, acctype=inventory[3])]
+ accts += [ Account.load(self.db, id = inventory[0]) ]
return accts
-
- def fix_inventory(self, item, amount):
- rv = self.db.execute_and_fetch(
- "SELECT public.fix_inventory(%s, %s, %s, %s, %s, %s)",
- [
- item.id,
- item.currency.id,
- self.excess.id,
- self.deficit.id,
- self.currency.id,
- amount,
- ],
- )[0]
-
- self.db.commit()
- return rv
-
- 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
-
- def consolidate(self):
- msg = self.db.execute_and_fetch(
- "SELECT public.make_consolidate_transaction(%s, %s, %s)",
- [self.excess.id, self.deficit.id, self.profits.id],
- )[0]
- if msg != None:
- print(msg)
- self.db.commit()
-
- def undo(self, oldtid):
- 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 6468bdb..0000000
--- a/brmbar3/schema/0009-shop-sell.sql
+++ /dev/null
@@ -1,149 +0,0 @@
---
--- 0009-shop-sell.sql
---
--- #9 - stored function for sell transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(8) THEN
-
--- return negative number on rate not found
-CREATE OR REPLACE FUNCTION public.find_buy_rate(
- IN i_item_id public.accounts.id%TYPE,
- IN i_other_id public.accounts.id%TYPE
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_rate public.exchange_rates.rate%TYPE;
- v_rate_dir public.exchange_rates.rate_dir%TYPE;
-BEGIN
- SELECT rate, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_item_id AND source = i_other_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
- IF v_rate_dir = 'target_to_source'::public.exchange_rate_direction THEN
- RETURN v_rate;
- ELSE
- RETURN 1/v_rate;
- END IF;
-EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RETURN -1;
-END;
-$$;
-
-
--- return negative number on rate not found
-CREATE OR REPLACE FUNCTION public.find_sell_rate(
- IN i_item_id public.accounts.id%TYPE,
- IN i_other_id public.accounts.id%TYPE
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_rate public.exchange_rates.rate%TYPE;
- v_rate_dir public.exchange_rates.rate_dir%TYPE;
-BEGIN
- SELECT rate, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_other_id AND source = i_item_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
- IF v_rate_dir = 'source_to_target'::public.exchange_rate_direction THEN
- RETURN v_rate;
- ELSE
- RETURN 1/v_rate;
- END IF;
-EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RETURN -1;
-END;
-$$;
-
-CREATE OR REPLACE FUNCTION public.create_transaction(
- i_responsible_id public.accounts.id%TYPE,
- i_description public.transactions.description%TYPE
-) RETURNS public.transactions.id%TYPE AS $$
-DECLARE
- new_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- INSERT INTO public.transactions (responsible, description)
- VALUES (i_responsible_id, i_description)
- RETURNING id INTO new_transaction_id;
- -- Return the new transaction ID
- RETURN new_transaction_id;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION public.sell_item(
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_user_id public.accounts.id%TYPE,
- i_target_currency_id public.currencies.id%TYPE,
- i_description TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_buy_rate NUMERIC;
- v_sell_rate NUMERIC;
- v_cost NUMERIC;
- v_profit NUMERIC;
- v_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Get the buy and sell rates from the stored functions
- v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
- v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
-
- -- Calculate cost and profit
- v_cost := i_amount * v_sell_rate;
- v_profit := i_amount * (v_sell_rate - v_buy_rate);
-
- -- Create a new transaction
- v_transaction_id := public.create_transaction(i_user_id, i_description);
-
- -- the item (decrease stock)
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'credit', i_item_id, i_amount,
- (SELECT "name" FROM public.accounts WHERE id = i_user_id));
-
- -- the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'debit', i_user_id, v_cost,
- (SELECT "name" FROM public.accounts WHERE id = i_item_id));
-
- -- the profit
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'debit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id));
-
- -- Return the cost
- RETURN v_cost;
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(9);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0010-shop-sell-for-cash.sql b/brmbar3/schema/0010-shop-sell-for-cash.sql
deleted file mode 100644
index 86fcf9c..0000000
--- a/brmbar3/schema/0010-shop-sell-for-cash.sql
+++ /dev/null
@@ -1,154 +0,0 @@
---
--- 0010-shop-sell-for-cash.sql
---
--- #10 - stored function for cash sell transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(9) THEN
-
-CREATE OR REPLACE FUNCTION brmbar_privileged.create_transaction(
- i_responsible_id public.accounts.id%TYPE,
- i_description public.transactions.description%TYPE
-) RETURNS public.transactions.id%TYPE AS $$
-DECLARE
- new_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- INSERT INTO public.transactions (responsible, description)
- VALUES (i_responsible_id, i_description)
- RETURNING id INTO new_transaction_id;
- -- Return the new transaction ID
- RETURN new_transaction_id;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION brmbar_privileged.sell_item_internal(
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_user_id public.accounts.id%TYPE,
- i_target_currency_id public.currencies.id%TYPE,
- i_other_memo TEXT,
- i_description TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_buy_rate NUMERIC;
- v_sell_rate NUMERIC;
- v_cost NUMERIC;
- v_profit NUMERIC;
- v_transaction_id public.transactions.id%TYPE;
- v_item_currency_id public.accounts.currency%TYPE;
-BEGIN
- -- Get item's currency
- SELECT currency
- INTO v_item_currency_id
- FROM public.accounts
- WHERE id=i_item_id;
-
- -- Get the buy and sell rates from the stored functions
- v_buy_rate := public.find_buy_rate(v_item_currency_id, i_target_currency_id);
- v_sell_rate := public.find_sell_rate(v_item_currency_id, i_target_currency_id);
-
- -- Calculate cost and profit
- v_cost := i_amount * v_sell_rate;
- v_profit := i_amount * (v_sell_rate - v_buy_rate);
-
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(i_user_id, i_description);
-
- -- the item (decrease stock)
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_item_id, i_amount,
- i_other_memo);
-
- -- the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'debit', i_user_id, v_cost,
- (SELECT "name" FROM public.accounts WHERE id = i_item_id));
-
- -- the profit
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id,
- 'debit',
- (SELECT id FROM public.accounts WHERE name = 'BrmBar Profits'),
- v_profit,
- (SELECT 'Margin on ' || "name" FROM public.accounts WHERE id = i_item_id));
-
- -- Return the cost
- RETURN v_cost;
-END;
-$$;
-
-CREATE OR REPLACE FUNCTION public.sell_item(
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_user_id public.accounts.id%TYPE,
- i_target_currency_id public.currencies.id%TYPE,
- i_description TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-BEGIN
- RETURN brmbar_privileged.sell_item_internal(i_item_id,
- i_amount,
- i_user_id,
- i_target_currency_id,
- (SELECT "name" FROM public.accounts WHERE id = i_user_id),
- i_description);
-END;
-$$;
-
-CREATE OR REPLACE FUNCTION public.sell_item_for_cash(
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_user_id public.accounts.id%TYPE,
- i_target_currency_id public.currencies.id%TYPE,
- i_description TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-BEGIN
- RETURN brmbar_privileged.sell_item_internal(i_item_id,
- i_amount,
- i_user_id,
- i_target_currency_id,
- 'Cash',
- i_description);
-END;
-$$;
-
-DROP FUNCTION public.create_transaction;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(10);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0011-shop-undo-sale.sql b/brmbar3/schema/0011-shop-undo-sale.sql
deleted file mode 100644
index 4427070..0000000
--- a/brmbar3/schema/0011-shop-undo-sale.sql
+++ /dev/null
@@ -1,102 +0,0 @@
---
--- 0011-shop-undo-sale.sql
---
--- #11 - stored function for sale undo transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(10) THEN
-
-CREATE OR REPLACE FUNCTION brmbar_privileged.create_transaction(
- i_responsible_id public.accounts.id%TYPE,
- i_description public.transactions.description%TYPE
-) RETURNS public.transactions.id%TYPE AS $$
-DECLARE
- new_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- INSERT INTO public.transactions (responsible, description)
- VALUES (i_responsible_id, i_description)
- RETURNING id INTO new_transaction_id;
- -- Return the new transaction ID
- RETURN new_transaction_id;
-END;
-$$ LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION public.undo_sale_of_item(
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_user_id public.accounts.id%TYPE,
- i_target_currency_id public.currencies.id%TYPE,
- i_description TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_buy_rate NUMERIC;
- v_sell_rate NUMERIC;
- v_cost NUMERIC;
- v_profit NUMERIC;
- v_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Get the buy and sell rates from the stored functions
- v_buy_rate := public.find_buy_rate(i_item_id, i_target_currency_id);
- v_sell_rate := public.find_sell_rate(i_item_id, i_target_currency_id);
-
- -- Calculate cost and profit
- v_cost := i_amount * v_sell_rate;
- v_profit := i_amount * (v_sell_rate - v_buy_rate);
-
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(i_user_id, i_description);
-
- -- the item (decrease stock)
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'debit', i_item_id, i_amount,
- (SELECT "name" || ' (sale undo)' FROM public.accounts WHERE id = i_user_id));
-
- -- the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'credit', i_user_id, v_cost,
- (SELECT "name" || ' (sale undo)' FROM public.accounts WHERE id = i_item_id));
-
- -- the profit
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'credit', (SELECT account_id FROM accounts WHERE name = 'BrmBar Profits'), v_profit, (SELECT 'Margin repaid on ' || "name" FROM public.accounts WHERE id = i_item_id));
-
- -- Return the cost
- RETURN v_cost;
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(11);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0012-shop-add-credit.sql b/brmbar3/schema/0012-shop-add-credit.sql
deleted file mode 100644
index 00318ae..0000000
--- a/brmbar3/schema/0012-shop-add-credit.sql
+++ /dev/null
@@ -1,64 +0,0 @@
---
--- 0012-shop-add-credit.sql
---
--- #12 - stored function for cash deposit transactions
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(11) THEN
-
-CREATE OR REPLACE FUNCTION public.add_credit(
- i_cash_account_id public.accounts.id%TYPE,
- i_credit NUMERIC,
- i_user_id public.accounts.id%TYPE,
- i_user_name TEXT
-) RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(i_user_id, 'BrmBar credit replenishment for ' || i_user_name);
- -- Debit cash (credit replenishment)
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'debit', i_cash_account_id, i_credit, i_user_name);
- -- Credit the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_user_id, i_credit, 'Credit replenishment');
-END;
-$$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(12);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0013-shop-withdraw-credit.sql b/brmbar3/schema/0013-shop-withdraw-credit.sql
deleted file mode 100644
index d379186..0000000
--- a/brmbar3/schema/0013-shop-withdraw-credit.sql
+++ /dev/null
@@ -1,64 +0,0 @@
---
--- 0013-shop-withdraw-credit.sql
---
--- #13 - stored function for cash withdrawal transactions
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(12) THEN
-
-CREATE OR REPLACE FUNCTION public.withdraw_credit(
- i_cash_account_id public.accounts.id%TYPE,
- i_credit NUMERIC,
- i_user_id public.accounts.id%TYPE,
- i_user_name TEXT
-) RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(i_user_id, 'BrmBar credit withdrawal for ' || i_user_name);
- -- Debit cash (credit replenishment)
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_cash_account_id, i_credit, i_user_name);
- -- Credit the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'debit', i_user_id, i_credit, 'Credit withdrawal');
-END;
-$$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(13);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0014-shop-transfer-credit.sql b/brmbar3/schema/0014-shop-transfer-credit.sql
deleted file mode 100644
index 7e13c03..0000000
--- a/brmbar3/schema/0014-shop-transfer-credit.sql
+++ /dev/null
@@ -1,58 +0,0 @@
---
--- 0014-shop-transfer-credit.sql
---
--- #14 - stored function for "credit" transfer transactions
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(13) THEN
-
-CREATE OR REPLACE FUNCTION public.transfer_credit(
- i_cash_account_id public.accounts.id%TYPE,
- i_credit NUMERIC,
- i_userfrom_id public.accounts.id%TYPE,
- i_userfrom_name TEXT,
- i_userto_id public.accounts.id%TYPE,
- i_userto_name TEXT
-) RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-BEGIN
- PERFORM public.add_credit(i_cash_account_id, i_credit, i_userto_id, i_userto_name);
- PERFORM public.withdraw_credit(i_cash_account_id, i_credit, i_userfrom_id, i_userfrom_name);
-END;
-$$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(14);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0015-shop-buy-for-cash.sql b/brmbar3/schema/0015-shop-buy-for-cash.sql
deleted file mode 100644
index aa66260..0000000
--- a/brmbar3/schema/0015-shop-buy-for-cash.sql
+++ /dev/null
@@ -1,85 +0,0 @@
---
--- 0015-shop-buy-for-cash.sql
---
--- #15 - stored function for cash-based stock replenishment transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(14) THEN
-
-CREATE OR REPLACE FUNCTION public.buy_for_cash(
- i_cash_account_id public.accounts.id%TYPE,
- i_item_id public.accounts.id%TYPE,
- i_amount INTEGER,
- i_target_currency_id public.currencies.id%TYPE,
- i_item_name TEXT
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_buy_rate NUMERIC;
- v_cost NUMERIC;
- v_transaction_id public.transactions.id%TYPE;
- v_item_currency_id public.accounts.currency%TYPE;
-BEGIN
- -- Get item's currency
- SELECT currency
- INTO STRICT v_item_currency_id
- FROM public.accounts
- WHERE id=i_item_id;
- -- Get the buy rates from the stored functions
- v_buy_rate := public.find_buy_rate(v_item_currency_id, i_target_currency_id);
-
- -- Calculate cost and profit
- v_cost := i_amount * v_buy_rate;
-
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(NULL,
- 'BrmBar stock replenishment of ' || i_amount || 'x ' || i_item_name || ' for cash');
-
- -- the item
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'debit', i_item_id, i_amount,
- 'Cash');
-
- -- the cash
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_cash_account_id, v_cost,
- i_item_name);
-
- -- Return the cost
- RETURN v_cost;
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(15);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0016-shop-receipt-to-credit.sql b/brmbar3/schema/0016-shop-receipt-to-credit.sql
deleted file mode 100644
index db77f7a..0000000
--- a/brmbar3/schema/0016-shop-receipt-to-credit.sql
+++ /dev/null
@@ -1,64 +0,0 @@
---
--- 0016-shop-buy-for-cash.sql
---
--- #16 - stored function for receipt reimbursement transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(15) THEN
-
-CREATE OR REPLACE FUNCTION public.receipt_reimbursement(
- i_profits_id public.accounts.id%TYPE,
- i_user_id public.accounts.id%TYPE,
- i_user_name public.accounts.name%TYPE,
- i_amount NUMERIC,
- i_description TEXT
-) RETURNS VOID
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_transaction_id public.transactions.id%TYPE;
-BEGIN
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(i_user_id,
- 'Receipt: ' || i_description);
- -- the "profit"
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_profits_id, i_amount, i_user_name);
- -- the user
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (v_transaction_id, 'credit', i_user_id, i_amount, 'Credit from receipt: ' || i_description);
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(16);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0017-shop-fix-inventory.sql b/brmbar3/schema/0017-shop-fix-inventory.sql
deleted file mode 100644
index a011892..0000000
--- a/brmbar3/schema/0017-shop-fix-inventory.sql
+++ /dev/null
@@ -1,159 +0,0 @@
---
--- 0017-shop-fix-inventory.sql
---
--- #17 - stored function for "fixing" inventory transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(16) THEN
-
-CREATE OR REPLACE FUNCTION public.compute_account_balance(
- i_account_id public.accounts.id%TYPE
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_crsum NUMERIC;
- v_dbsum NUMERIC;
-BEGIN
- SELECT
- COALESCE(SUM(CASE WHEN side='credit' THEN amount ELSE 0 END),0) crsum,
- COALESCE(SUM(CASE WHEN side='debit' THEN amount ELSE 0 END),0) dbsum
- INTO v_crsum, v_dbsum
- FROM public.transaction_splits ts WHERE ts.account=i_account_id;
- RETURN v_dbsum - v_crsum;
-END; $$;
-
-CREATE OR REPLACE FUNCTION brmbar_privileged.fix_account_balance(
- IN i_account_id public.accounts.id%TYPE,
- IN i_account_currency_id public.currencies.id%TYPE,
- IN i_excess_id public.accounts.id%TYPE,
- IN i_deficit_id public.accounts.id%TYPE,
- IN i_shop_currency_id public.currencies.id%TYPE,
- IN i_amount_in_reality NUMERIC
-) RETURNS BOOLEAN
-VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
-DECLARE
- v_amount_in_system NUMERIC;
- v_buy_rate NUMERIC;
- v_currency_id public.currencies.id%TYPE;
- v_diff NUMERIC;
- v_buy_total NUMERIC;
- v_ntrn_id public.transactions.id%TYPE;
- v_transaction_memo TEXT;
- v_item_name TEXT;
- v_excess_memo TEXT;
- v_deficit_memo TEXT;
-
- v_old_trn public.transactions%ROWTYPE;
- v_old_split public.transaction_splits%ROWTYPE;
-BEGIN
- v_amount_in_system := public.compute_account_balance(i_account_id);
- IF i_account_currency_id <> i_shop_currency_id THEN
- v_buy_rate := public.find_buy_rate(i_item_id, i_shop_currency_id);
- ELSE
- v_buy_rate := 1;
- END IF;
-
- v_diff := ABS(i_amount_in_reality - v_amount_in_system);
- v_buy_total := v_buy_rate * v_diff;
- -- compute memo strings
- IF i_item_id = 1 THEN -- cash account recognized by magic id
- -- fixing cash
- v_transaction_memo :=
- 'BrmBar cash inventory fix of ' || v_amount_in_system
- || ' in system to ' || i_amount_in_reality || ' in reality';
- v_excess_memo := 'Inventory cash fix excess.';
- v_deficit_memo := 'Inventory fix deficit.';
- ELSE
- -- fixing other account
- SELECT "name" INTO v_item_name FROM public.accounts WHERE id = i_account_id;
- v_transaction_memo :=
- 'BrmBar inventory fix of ' || v_amount_in_system || 'pcs '
- || v_item_name
- || ' in system to ' || i_amount_in_reality || 'pcs in reality';
- v_excess_memo := 'Inventory fix excess ' || v_item_name;
- v_deficit_memo := 'Inventory fix deficit ' || v_item_name;
- END IF;
- -- create transaction based on the relation between counting and accounting
- IF i_amount_in_reality > v_amount_in_system THEN
- v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'debit', i_item_id, v_diff, 'Inventory fix excess');
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'credit', i_excess_id, v_buy_total, v_excess_memo);
- RETURN TRUE;
- ELSIF i_amount_in_reality < v_amount_in_system THEN
- v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'credit', i_item_id, v_diff, 'Inventory fix deficit');
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'debit', i_deficit_id, v_buy_total, v_deficit_memo);
- RETURN TRUE;
- ELSIF i_account_id <> 1 THEN -- cash account recognized by magic id
- -- record that everything is going on swimmingly only for noncash accounts (WTF)
- v_ntrn_id := brmbar_privileged.create_transaction(NULL, v_transaction_memo);
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'debit', i_item_id, 0, 'Inventory fix - amount was correct');
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, 'credit', i_item_id, 0, 'Inventory fix - amount was correct');
- RETURN FALSE;
- END IF;
- RETURN FALSE;
-END;
-$fn$;
-
-
-CREATE OR REPLACE FUNCTION public.fix_inventory(
- IN i_account_id public.accounts.id%TYPE,
- IN i_account_currency_id public.currencies.id%TYPE,
- IN i_excess_id public.accounts.id%TYPE,
- IN i_deficit_id public.accounts.id%TYPE,
- IN i_shop_currency_id public.currencies.id%TYPE,
- IN i_amount_in_reality NUMERIC
-) RETURNS BOOLEAN
-VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
-BEGIN
- RETURN brmbar_privileged.fix_account_balance(
- i_account_id,
- i_account_currency_id,
- i_excess_id,
- i_deficit_id,
- i_shop_currency_id,
- i_amount_in_reality
- );
-END;
-$fn$;
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(17);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0018-shop-fix-cash.sql b/brmbar3/schema/0018-shop-fix-cash.sql
deleted file mode 100644
index 5ced17d..0000000
--- a/brmbar3/schema/0018-shop-fix-cash.sql
+++ /dev/null
@@ -1,60 +0,0 @@
---
--- 0018-shop-fix-cash.sql
---
--- #18 - stored function for "fixing cash" transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(17) THEN
-
-CREATE OR REPLACE FUNCTION public.fix_cash(
- IN i_excess_id public.accounts.id%TYPE,
- IN i_deficit_id public.accounts.id%TYPE,
- IN i_shop_currency_id public.currencies.id%TYPE,
- IN i_amount_in_reality NUMERIC
-) RETURNS BOOLEAN
-VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
-BEGIN
- RETURN brmbar_privileged.fix_account_balance(
- 1,
- 1,
- i_excess_id,
- i_deficit_id,
- i_shop_currency_id,
- i_amount_in_reality
- );
-END;
-$fn$;
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(18);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0019-shop-consolidate.sql b/brmbar3/schema/0019-shop-consolidate.sql
deleted file mode 100644
index db06685..0000000
--- a/brmbar3/schema/0019-shop-consolidate.sql
+++ /dev/null
@@ -1,82 +0,0 @@
---
--- 0019-shop-consolidate.sql
---
--- #19 - stored function for "consolidation" transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(18) THEN
-
-CREATE OR REPLACE FUNCTION public.make_consolidate_transaction(
- i_excess_id public.accounts.id%TYPE,
- i_deficit_id public.accounts.id%TYPE,
- i_profits_id public.accounts.id%TYPE
-) RETURNS TEXT
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_transaction_id public.transactions.id%TYPE;
- v_excess_balance NUMERIC;
- v_deficit_balance NUMERIC;
- v_ret TEXT;
-BEGIN
- v_ret := NULL;
- -- Create a new transaction
- v_transaction_id := brmbar_privileged.create_transaction(NULL,
- 'BrmBar inventory consolidation');
- v_excess_balance := public.compute_account_balance(i_excess_id);
- v_deficit_balance := public.compute_account_balance(i_deficit_id);
- IF v_excess_balance <> 0 THEN
- v_ret := 'Excess balance ' || -v_excess_balance || ' debited to profit';
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'debit', i_excess_id, -v_excess_balance,
- 'Excess balance added to profit.');
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'debit', i_profits_id, -v_excess_balance,
- 'Excess balance added to profit.');
- END IF;
- IF v_deficit_balance <> 0 THEN
- v_ret := COALESCE(v_ret, '');
- v_ret := v_ret || 'Deficit balance ' || v_deficit_balance || ' credited to profit';
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'credit', i_deficit_id, v_deficit_balance,
- 'Deficit balance removed from profit.');
- INSERT INTO public.transaction_splits (transaction, side, account, amount, memo)
- VALUES (i_transaction_id, 'credit', i_profits_id, v_deficit_balance,
- 'Deficit balance removed from profit.');
- END IF;
- RETURN v_ret;
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(19);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0020-shop-undo.sql b/brmbar3/schema/0020-shop-undo.sql
deleted file mode 100644
index d2d12b3..0000000
--- a/brmbar3/schema/0020-shop-undo.sql
+++ /dev/null
@@ -1,64 +0,0 @@
---
--- 0020-shop-undo.sql
---
--- #20 - stored function for undo transaction
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(19) THEN
-
-CREATE OR REPLACE FUNCTION public.undo_transaction(
- IN i_id public.transactions.id%TYPE)
-RETURNS public.transactions.id%TYPE
-VOLATILE NOT LEAKPROOF LANGUAGE plpgsql AS $fn$
-DECLARE
- v_ntrn_id public.transactions.id%TYPE;
- v_old_trn public.transactions%ROWTYPE;
- v_old_split public.transaction_splits%ROWTYPE;
-BEGIN
- SELECT * INTO v_old_trn FROM public.transactions WHERE id = i_id;
- INSERT INTO transactions ("description") VALUES ('undo '||i_id||' ('||v_old_trn.description||')') RETURNING id into v_ntrn_id;
- FOR v_old_split IN
- SELECT * FROM transaction_splits WHERE "transaction" = i_id
- LOOP
- INSERT INTO transaction_splits ("transaction", "side", "account", "amount", "memo")
- VALUES (v_ntrn_id, v_old_split.side, v_old_split.account, -v_old_split.amount,
- 'undo ' || v_old_split.id || ' (' || v_old_split.memo || ')' );
- END LOOP;
- RETURN v_ntrn_id;
-END;
-$fn$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(20);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0021-constraints-on-numeric-columns.sql b/brmbar3/schema/0021-constraints-on-numeric-columns.sql
deleted file mode 100644
index 524f08a..0000000
--- a/brmbar3/schema/0021-constraints-on-numeric-columns.sql
+++ /dev/null
@@ -1,48 +0,0 @@
---
--- 0021-constraints-on-numeric-columns.sql
---
--- #21 - stored function for adding constraints to numeric columns
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(20) THEN
-
-ALTER TABLE public.transaction_splits ADD CONSTRAINT amount_check
- CHECK (amount NOT IN ('Infinity'::numeric, '-Infinity'::numeric, 'NaN'::numeric));
-
-ALTER TABLE public.exchange_rates ADD CONSTRAINT rate_check
- CHECK (rate NOT IN ('Infinity'::numeric, '-Infinity'::numeric, 'NaN'::numeric));
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(21);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
-
diff --git a/brmbar3/schema/0022-shop-init.sql b/brmbar3/schema/0022-shop-init.sql
deleted file mode 100644
index 55fe098..0000000
--- a/brmbar3/schema/0022-shop-init.sql
+++ /dev/null
@@ -1,85 +0,0 @@
---
--- 0022-shop-init.sql
---
--- #22 - stored function for initializing Shop.py
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-DECLARE
- v INTEGER;
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(21) THEN
-
-
-SELECT COUNT(1) INTO v
- FROM pg_catalog.pg_type typ
- INNER JOIN pg_catalog.pg_namespace nsp
- ON nsp.oid = typ.typnamespace
- WHERE nsp.nspname = 'brmbar_privileged'
- AND typ.typname='shop_class_initialization_data_type';
-
-IF v>0 THEN
- RAISE NOTICE 'Changing type shop_class_initialization_data_type';
- DROP TYPE brmbar_privileged.shop_class_initialization_data_type CASCADE;
-ELSE
- RAISE NOTICE 'Creating type shop_class_initialization_data_type';
-END IF;
-
-CREATE TYPE brmbar_privileged.shop_class_initialization_data_type
-AS (
- currency_id INTEGER, --public.currencies.id%TYPE,
- profits_id INTEGER, --public.accounts.id%TYPE,
- cash_id INTEGER, --public.accounts.id%TYPE,
- excess_id INTEGER, --public.accounts.id%TYPE,
- deficit_id INTEGER --public.accounts.id%TYPE
-);
-
-CREATE OR REPLACE FUNCTION public.shop_class_initialization_data()
-RETURNS brmbar_privileged.shop_class_initialization_data_type
-LANGUAGE plpgsql
-AS
-$$
-DECLARE
- rv brmbar_privileged.shop_class_initialization_data_type;
-BEGIN
- rv.currency_id := 1;
- SELECT id INTO rv.profits_id FROM public.accounts WHERE name = 'BrmBar Profits';
- SELECT id INTO rv.cash_id FROM public.accounts WHERE name = 'BrmBar Cash';
- SELECT id INTO rv.excess_id FROM public.accounts WHERE name = 'BrmBar Excess';
- SELECT id INTO rv.deficit_id FROM public.accounts WHERE name = 'BrmBar Deficit';
- RETURN rv;
-END;
-$$;
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(22);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
-
diff --git a/brmbar3/schema/0023-inventory-balance.sql b/brmbar3/schema/0023-inventory-balance.sql
deleted file mode 100644
index 2072812..0000000
--- a/brmbar3/schema/0023-inventory-balance.sql
+++ /dev/null
@@ -1,94 +0,0 @@
---
--- 0023-inventory-balance.sql
---
--- #23 - stored function for total inventory balance
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(22) THEN
-
-CREATE OR REPLACE VIEW brmbar_privileged.debit_balances AS
-SELECT
- ts.account AS debit_account,
- SUM(ts.amount) AS debit_sum
-FROM public.transaction_splits ts
-WHERE (ts.side = 'debit'::public.transaction_split_side)
-GROUP BY ts.account;
-
-CREATE OR REPLACE VIEW brmbar_privileged.credit_balances AS
-SELECT
- ts.account AS credit_account,
- SUM(ts.amount) AS credit_sum
-FROM public.transaction_splits ts
-WHERE (ts.side = 'credit'::public.transaction_split_side)
-GROUP BY ts.account;
-
-/*
- CASE
- WHEN (ts.side = 'credit'::public.transaction_split_side) THEN (- ts.amount)
- ELSE ts.amount
- END AS amount,
- a.currency,
- ts.memo
- FROM (public.transaction_splits ts
- LEFT JOIN public.accounts a ON ((a.id = ts.account)))
- ORDER BY ts.id;
-*/
-
-CREATE OR REPLACE FUNCTION public.inventory_balance()
-RETURNS DECIMAL(12,2)
-VOLATILE NOT LEAKPROOF LANGUAGE plpgsql SECURITY DEFINER AS $fn$
-DECLARE
- rv DECIMAL(12,2);
-BEGIN
- WITH inventory_balances AS (
- SELECT COALESCE(credit_sum, 0) * public.find_buy_rate(a.currency, 1) as credit_sum,
- COALESCE(debit_sum, 0) * public.find_buy_rate(a.currency, 1) as debit_sum,
- COALESCE(credit_account, debit_account) as cd_account
- FROM brmbar_privileged.credit_balances cb
- FULL OUTER JOIN brmbar_privileged.debit_balances db
- ON (debit_account = credit_account)
- LEFT JOIN public.accounts a
- ON (a.id = COALESCE(credit_account, debit_account))
- WHERE a.acctype = 'inventory'::public.account_type
- )
- SELECT SUM(debit_sum) - SUM(credit_sum) INTO rv
- FROM inventory_balances;
-
- RETURN rv;
-END;
-$fn$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(23);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0024-find-rates-fix.sql b/brmbar3/schema/0024-find-rates-fix.sql
deleted file mode 100644
index 70bab06..0000000
--- a/brmbar3/schema/0024-find-rates-fix.sql
+++ /dev/null
@@ -1,92 +0,0 @@
---
--- 0024-find-rates-fix.sql
---
--- #24 - fix stored functions find_buy_rate and find_sell_rate
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(23) THEN
-
-DROP FUNCTION IF EXISTS public.find_buy_rate(integer,integer);
-DROP FUNCTION IF EXISTS public.find_sell_rate(integer,integer);
-
-CREATE OR REPLACE FUNCTION public.find_buy_rate(
- IN i_item_currency_id public.accounts.id%TYPE,
- IN i_other_currency_id public.accounts.id%TYPE
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_rate public.exchange_rates.rate%TYPE;
- v_rate_dir public.exchange_rates.rate_dir%TYPE;
-BEGIN
- SELECT rate, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_item_currency_id AND source = i_other_currency_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
- IF v_rate_dir = 'target_to_source'::public.exchange_rate_direction THEN
- RETURN v_rate;
- ELSE
- RETURN 1/v_rate;
- END IF;
- /* propagate error
-EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RETURN -1;
- */
-END;
-$$;
-
--- return negative number on rate not found
-CREATE OR REPLACE FUNCTION public.find_sell_rate(
- IN i_item_currency_id public.accounts.id%TYPE,
- IN i_other_currency_id public.accounts.id%TYPE
-) RETURNS NUMERIC
-LANGUAGE plpgsql
-AS $$
-DECLARE
- v_rate public.exchange_rates.rate%TYPE;
- v_rate_dir public.exchange_rates.rate_dir%TYPE;
-BEGIN
- SELECT rate, rate_dir INTO STRICT v_rate, v_rate_dir FROM public.exchange_rates WHERE target = i_other_currency_id AND source = i_item_currency_id AND valid_since <= NOW() ORDER BY valid_since DESC LIMIT 1;
- IF v_rate_dir = 'source_to_target'::public.exchange_rate_direction THEN
- RETURN v_rate;
- ELSE
- RETURN 1/v_rate;
- END IF;
- /* propagate error
-EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RETURN -1;
- */
-END;
-$$;
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(24);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/schema/0025-load-account.sql b/brmbar3/schema/0025-load-account.sql
deleted file mode 100644
index 3076e0b..0000000
--- a/brmbar3/schema/0025-load-account.sql
+++ /dev/null
@@ -1,111 +0,0 @@
---
--- 0025-load-account.sql
---
--- #25 - stored procedures for account loading
---
--- ISC License
---
--- Copyright 2023-2025 Brmlab, z.s.
--- TMA
---
--- Permission to use, copy, modify, and/or distribute this software
--- for any purpose with or without fee is hereby granted, provided
--- that the above copyright notice and this permission notice appear
--- in all copies.
---
--- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
--- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
--- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
--- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
--- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
--- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
--- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
--- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
-
--- To require fully-qualified names
-SELECT pg_catalog.set_config('search_path', '', false);
-
-DO $upgrade_block$
-DECLARE
- v INTEGER;
-BEGIN
-
-IF brmbar_privileged.has_exact_schema_version(24) THEN
-
-SELECT COUNT(1) INTO v
- FROM pg_catalog.pg_type typ
- INNER JOIN pg_catalog.pg_namespace nsp
- ON nsp.oid = typ.typnamespace
- WHERE nsp.nspname = 'brmbar_privileged'
- AND typ.typname='account_class_initialization_data_type';
-
-IF v>0 THEN
- RAISE NOTICE 'Changing type account_class_initialization_data_type';
- DROP TYPE brmbar_privileged.account_class_initialization_data_type CASCADE;
-ELSE
- RAISE NOTICE 'Creating type account_class_initialization_data_type';
-END IF;
-
-SELECT COUNT(1) INTO v
- FROM pg_catalog.pg_type typ
- INNER JOIN pg_catalog.pg_namespace nsp
- ON nsp.oid = typ.typnamespace
- WHERE nsp.nspname = 'brmbar_privileged'
- AND typ.typname='account_load_type';
-
-IF v>0 THEN
- RAISE NOTICE 'Changing type account_load_type';
- DROP TYPE brmbar_privileged.account_load_type CASCADE;
-ELSE
- RAISE NOTICE 'Creating type account_load_type';
-END IF;
-
-CREATE TYPE brmbar_privileged.account_load_type
- AS ENUM ('by_id', 'by_barcode');
-
-CREATE TYPE brmbar_privileged.account_class_initialization_data_type
-AS (
- account_id INTEGER, --public.accounts.id%TYPE,
- account_name TEXT, --public.accounts.id%TYPE,
- account_acctype public.account_type,
- currency_id INTEGER, --public.currencies.id%TYPE,
- currency_name TEXT
-);
-
-CREATE OR REPLACE FUNCTION public.account_class_initialization_data(
- load_by brmbar_privileged.account_load_type,
- i_id INTEGER,
- i_barcode TEXT)
-RETURNS brmbar_privileged.account_class_initialization_data_type
-LANGUAGE plpgsql
-AS
-$$
-DECLARE
- rv brmbar_privileged.account_class_initialization_data_type;
-BEGIN
- IF load_by = 'by_id' THEN
- SELECT a.id, a.name, a.acctype, c.id, c.name
- INTO STRICT rv.account_id, rv.account_name, rv.account_acctype, rv.currency_id, rv.currency_name
- FROM public.accounts a JOIN public.currencies c ON a.currency = c.id
- WHERE a.id = i_id;
- ELSE -- by_barcode
- SELECT a.id, a.name, a.acctype, c.id, c.name
- INTO STRICT rv.account_id, rv.account_name, rv.account_acctype, rv.currency_id, rv.currency_name
- FROM public.accounts a JOIN public.currencies c ON a.currency = c.id JOIN public.barcodes b ON b.account = a.id
- WHERE b.barcode = i_barcode;
- END IF;
- --
- RETURN rv;
-END;
-$$;
-
-
-
-PERFORM brmbar_privileged.upgrade_schema_version_to(25);
-END IF;
-
-END;
-$upgrade_block$;
-
--- vim: set ft=plsql :
diff --git a/brmbar3/test--currency-rates.py b/brmbar3/test--currency-rates.py
deleted file mode 100644
index 9ef93bb..0000000
--- a/brmbar3/test--currency-rates.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/python3
-
-import sys
-import subprocess
-
-#from brmbar import Database
-#from brmbar import Currency
-
-from contextlib import closing
-import psycopg2
-from brmbar.Database import Database
-from brmbar.Currency import Currency
-import math
-
-#import brmbar
-
-
-
-def approx_equal(a, b, tol=1e-6):
- """Check if two (buy, sell) rate tuples are approximately equal."""
- return (
- isinstance(a, tuple) and isinstance(b, tuple) and
- math.isclose(a[0], b[0], abs_tol=tol) and
- math.isclose(a[1], b[1], abs_tol=tol)
- )
-
-def compare_exceptions(e1, e2):
- """Compare exception types and messages."""
- return type(e1) == type(e2) and str(e1) == str(e2)
-
-def main():
- db = Database("dbname=brmbar")
-
- # Get all currencies
- with closing(db.db_conn.cursor()) as cur:
- cur.execute("SELECT id, name FROM currencies")
- currencies = cur.fetchall()
-
- # Build Currency objects
- currency_objs = [Currency(db, id, name) for id, name in currencies]
-
- # Test all currency pairs
- for c1 in currency_objs:
- for c2 in currency_objs:
- #if c1.id == c2.id:
- # continue
-
- try:
- rates1 = c1.rates(c2)
- exc1 = None
- except (RuntimeError, NameError) as e1:
- rates1 = None
- exc1 = e1
-
- try:
- rates2 = c1.rates2(c2)
- exc2 = None
- except (RuntimeError, NameError) as e2:
- rates2 = None
- exc2 = e2
-
- if exc1 or exc2:
- if not compare_exceptions(exc1, exc2):
- print(f"[EXCEPTION DIFFERENCE] {c1.name} -> {c2.name}")
- print(f" rates() exception: {type(exc1).__name__}: {exc1}")
- print(f" rates2() exception: {type(exc2).__name__}: {exc2}")
- elif not approx_equal(rates1, rates2):
- print(f"[VALUE DIFFERENCE] {c1.name} -> {c2.name}")
- print(f" rates(): {rates1}")
- print(f" rates2(): {rates2}")
-
-if __name__ == "__main__":
- main()
diff --git a/brmbar3/uklid-refill.sh b/brmbar3/uklid-refill.sh
deleted file mode 100644
index 898a081..0000000
--- a/brmbar3/uklid-refill.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-if [ `./brmbar-cli.py iteminfo uklid|grep -o '[0-9]*.[0-9]* pcs'|cut -d '.' -f 1` -eq 0 ]; then
- BOUNTY=`./brmbar-cli.py restock uklid 1 | grep -o 'take -[0-9]*'|grep -o '[0-9]*'`
- echo "Brmlab cleanup bounty for ${BOUNTY}CZK!!!"|ssh jenda@fry.hrach.eu
-fi
diff --git a/brmbar3/uklid-watchdog.sh b/brmbar3/uklid-watchdog.sh
deleted file mode 100644
index 7f86870..0000000
--- a/brmbar3/uklid-watchdog.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-LASTIDF=/home/brmlab/uklid.last
-
-LASTID=`cat $LASTIDF 2>/dev/null || echo 0`
-
-
-RES=`psql brmbar -Atq -c "select id,description from transactions where id>$LASTID and description like 'BrmBar sale of 1x uklid%' LIMIT 1;"`
-if [ ! -z "$RES" ]; then
- LASTID=`echo "$RES"|cut -d '|' -f 1`
- echo $LASTID > $LASTIDF
-
- WINNER=`echo "$RES"|grep -o 'to [^ ]*'|cut -d ' ' -f 2`
- if [ -z "$WINNER" ]; then
- WINNER="anonymous hunter"
- fi
- echo "Brmlab cleanup bounty was claimed by $WINNER! Thanks!"|ssh -p 110 jenda@coralmyn.hrach.eu
-fi
-