forked from brmlab/brmbar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbrmbar-cli.py
executable file
·260 lines (211 loc) · 8.89 KB
/
brmbar-cli.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#!/usr/bin/python3
import sys
from brmbar import Database
import brmbar
def help():
print("""BrmBar v3 (c) Petr Baudis <[email protected]> 2012-2013
Usage: brmbar-cli.py COMMAND ARGS...
1. Commands pertaining the standard operation
showcredit USER
changecredit USER +-AMT
sellitem {USER|"cash"} ITEM +-AMT
You can use negative AMT to undo a sale.
restock ITEM AMT
userinfo USER
userlog USER TIMESTAMP
iteminfo ITEM
2. Management commands
listusers
List all user accounts in the system.
listitems
List all item accounts in the system.
stats
A set of various balances as shown in the Management
screen of the GUI.
adduser USER
Add user (debt) account with given username.
undo TRANSID
Commit a transaction that reverses all splits of a transaction with
a given id (to find out that id: select * from transaction_cashsums;)
3. Inventorization
inventory ITEM1 NEW_AMOUNT1 ITEM2 NEW_AMOUNT2
Inventory recounting (fixing the number of items)
inventory-interactive
Launches interactive mode for performing inventory with barcode reader
fixcash AMT
Fixes the cash and puts money difference into excess or deficit account
consolidate
Wraps up inventory + cash recounting, transferring the excess and
deficit accounts balance to the profits account and resetting them
USER and ITEM may be barcodes or account ids. AMT may be
both positive and negative amount (big difference to other
user interfaces; you can e.g. undo a sale!).
For users, you can use their name as USER as their username
is also the barcode. For items, use listitems command first
to find out the item id.
EXAMPLES:
Transfer 35Kc from pasky to sachy:
$ ./brmbar-cli.py changecredit pasky -35
$ ./brmbar-cli.py changecredit sachy +35
Buy one RaspberryPi for cash from commandline:
$ ./brmbar-cli.py listitems | grep -i raspberry
Raspberry Pi 2 1277 1.00 pcs
$ ./brmbar-cli.py sellitem cash 1277 1
""")
sys.exit(1)
def load_acct(inp):
acct = None
if inp.isdigit():
acct = brmbar.Account.load(db, id = inp)
if acct is None:
acct = brmbar.Account.load_by_barcode(db, inp)
if acct is None:
print("Cannot map account " + inp, file=sys.stderr)
exit(1)
return acct
def load_user(inp):
acct = load_acct(inp)
if acct.acctype != "debt":
print("Bad account " + inp + " type " + acct.acctype, file=sys.stderr)
exit(1)
return acct
def load_item(inp):
acct = load_acct(inp)
if acct.acctype != "inventory":
print("Bad account " + inp + " type " + acct.acctype, file=sys.stderr)
exit(1)
return acct
db = Database.Database("dbname=brmbar")
shop = brmbar.Shop.new_with_defaults(db)
currency = shop.currency
if len(sys.argv) <= 1:
help()
if sys.argv[1] == "showcredit":
acct = load_user(sys.argv[2])
print("{}: {}".format(acct.name, acct.negbalance_str()))
elif sys.argv[1] == "changecredit":
acct = load_user(sys.argv[2])
amt = int(sys.argv[3])
if amt > 0:
shop.add_credit(credit = amt, user = acct)
elif amt < 0:
shop.withdraw_credit(credit = -amt, user = acct)
print("{}: {}".format(acct.name, acct.negbalance_str()))
elif sys.argv[1] == "sellitem":
if sys.argv[2] == "cash":
uacct = shop.cash
else:
uacct = load_user(sys.argv[2])
iacct = load_item(sys.argv[3])
amt = int(sys.argv[4])
if amt > 0:
if uacct == shop.cash:
shop.sell_for_cash(item = iacct, amount = amt)
else:
shop.sell(item = iacct, user = uacct, amount = amt)
elif amt < 0:
shop.undo_sale(item = iacct, user = uacct, amount = -amt)
print("{}: {}".format(uacct.name, uacct.balance_str() if uacct == shop.cash else uacct.negbalance_str()))
print("{}: {}".format(iacct.name, iacct.balance_str()))
elif sys.argv[1] == "userinfo":
acct = load_user(sys.argv[2])
print("{} (id {}): {}".format(acct.name, acct.id, acct.negbalance_str()))
res = db.execute_and_fetchall("SELECT barcode FROM barcodes WHERE account = %s", [acct.id])
print("Barcodes: " + ", ".join(map((lambda r: r[0]), res)))
elif sys.argv[1] == "userlog":
acct = load_user(sys.argv[2])
timestamp = sys.argv[3]
res = db.execute_and_fetchall("SELECT transactions.time,transactions.description FROM transactions INNER JOIN accounts ON accounts.id=transactions.responsible WHERE accounts.name=%s and time > TIMESTAMP %s ORDER BY time", [acct.name,timestamp])
for transaction in res:
print("{}\t{}\t".format(transaction[0],transaction[1]))
elif sys.argv[1] == "iteminfo":
acct = load_item(sys.argv[2])
print("{} (id {}): {} pcs".format(acct.name, acct.id, acct.balance()))
(buy, sell) = acct.currency.rates(currency)
print("Buy: " + currency.str(buy) + " Sell: " + currency.str(sell));
res = db.execute_and_fetchall("SELECT barcode FROM barcodes WHERE account = %s", [acct.id])
print("Barcodes: " + ", ".join(map((lambda r: r[0]), res)))
elif sys.argv[1] == "listusers":
for acct in shop.account_list("debt"):
print("{}\t{}\t{}".format(acct.name, acct.id, acct.negbalance_str()))
elif sys.argv[1] == "listitems":
for acct in shop.account_list("inventory"):
print("{}\t{}\t{} pcs".format(acct.name, acct.id, acct.balance()))
elif sys.argv[1] == "stats":
print("--- Material Assets ---")
print("Cash: {}".format(shop.cash.balance_str()))
print("Overflow: {}".format(shop.currency.str(shop.credit_balance(overflow='only'))))
print("Inventory: {}".format(shop.inventory_balance_str()))
print("--- Logical Accounts ---")
print("Credit: {}".format(shop.credit_negbalance_str(overflow='exclude')))
print("Profit: {}".format(shop.profits.balance_str()))
print("Fixups: {} (excess {}, deficit {})".format(
-shop.excess.balance() - shop.deficit.balance(),
shop.excess.negbalance_str(),
shop.deficit.balance_str()))
elif sys.argv[1] == "adduser":
acct = brmbar.Account.create(db, sys.argv[2], brmbar.Currency.load(db, id = 1), 'debt')
acct.add_barcode(sys.argv[2]) # will commit
print("{}: id {}".format(acct.name, acct.id));
elif sys.argv[1] == "undo":
newtid = shop.undo(int(sys.argv[2]))
print("Transaction %d undone by reverse transaction %d" % (int(sys.argv[2]), newtid))
elif sys.argv[1] == "inventory":
if (len(sys.argv) % 2 != 0 or len(sys.argv) < 4):
print ("Invalid number of parameters, count your parameters.")
else:
for i in range(2, len(sys.argv), 2):
iacct = load_item(sys.argv[i])
iamt = int(sys.argv[i+1])
print("Current state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
if shop.fix_inventory(item = iacct, amount = iamt):
print("New state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
else:
print ("No action needed amount is correct.")
elif sys.argv[1] == "inventory-interactive":
print("Inventory interactive mode. To exit interactive mode just enter empty barcode")
while True:
barcode = str(input("Enter barcode:"))
fuckyou = input("fuckyou")
if barcode == "":
break
iacct = brmbar.Account.load_by_barcode(db, barcode)
amount = str(input("What is the amount of {} in reality (expected: {} pcs):".format(iacct.name, iacct.balance())))
if amount == "":
break
elif int(amount) > 10000:
print("Ignoring too high amount {}, assuming barcode was mistakenly scanned instead".format(amount))
else:
iamt = int(amount)
print("Current state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
if shop.fix_inventory(item = iacct, amount = iamt):
print("New state {} (id {}): {} pcs".format(iacct.name, iacct.id, iacct.balance()))
else:
print("No action needed, amount is correct.")
print("End of processing. Bye")
elif sys.argv[1] == "fixcash" or sys.argv[1] == "changecash":
if (len(sys.argv) != 3):
print ("Invalid number of parameters, check your parameters.")
else:
print("Current Cash is : {}".format(shop.cash.balance_str()))
iamt = int(sys.argv[2])
if shop.fix_cash(amount = iamt):
print("New Cash is : {}".format(shop.cash.balance_str()))
else:
print ("No action needed amount is the same.")
elif sys.argv[1] == "consolidate":
if (len(sys.argv) != 2):
print ("Invalid number of parameters, check your parameters.")
else:
shop.consolidate()
elif sys.argv[1] == "restock":
if (len(sys.argv) != 4):
print ("Invalid number of parameters, check your parameters.")
else:
iacct = load_item(sys.argv[2])
oldbal = iacct.balance()
amt = int(sys.argv[3])
cash = shop.buy_for_cash(iacct, amt);
print("Old amount {}, increased by {}, take {} from cashbox".format(oldbal, amt, cash))
else:
help()