brmbar/brmbar3/brmbar/Shop.py

248 lines
8.1 KiB
Python

import brmbar
from .Currency import Currency
from .Account import Account
import logging
logger = logging.getLogger(__name__)
class 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
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)
@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,
)
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]
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]
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],
)
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],
)
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):
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]
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]
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],
)
transaction = transaction[0]
return transaction
def credit_balance(self, overflow=None):
# We assume all debt accounts share a currency
sumselect = """
SELECT SUM(ts.amount)
FROM accounts AS a
LEFT JOIN transaction_splits AS ts ON a.id = ts.account
WHERE a.acctype = %s AND ts.side = %s
"""
if overflow is not None:
sumselect += (
" AND a.name "
+ ("NOT " if overflow == "exclude" else "")
+ " LIKE '%%-overflow'"
)
cur = self.db.execute_and_fetch(sumselect, ["debt", "debit"])
debit = cur[0] or 0
credit = self.db.execute_and_fetch(sumselect, ["debt", "credit"])
credit = credit[0] or 0
return debit - credit
def credit_negbalance_str(self, overflow=None):
return self.currency.str(-self.credit_balance(overflow=overflow))
def 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
def inventory_balance_str(self):
return self.currency.str(self.inventory_balance())
def account_list(self, acctype, like_str="%%"):
"""list all accounts (people or items, as per acctype)"""
accts = []
cur = self.db.execute_and_fetchall(
"SELECT id FROM accounts WHERE acctype = %s AND name ILIKE %s ORDER BY name ASC",
[acctype, like_str],
)
# FIXME: sanitize input like_str ^
for inventory in cur:
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