forked from brmlab/brmbar-github
248 lines
8.1 KiB
Python
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
|