mirror of
https://codeberg.org/metamuffin/abrechenbarkeit.git
synced 2024-12-29 00:04:35 +00:00
better log, barcode scanner support
This commit is contained in:
parent
a7868afbcf
commit
c3dadcc5a3
1 changed files with 109 additions and 49 deletions
158
strichliste.lua
158
strichliste.lua
|
@ -41,13 +41,22 @@ local stylesheet = [[
|
|||
.amount-neg { color: red; }
|
||||
nav h2 { display: inline-block }
|
||||
.notif { padding: 0.5em; margin: 0.5em; background-color: #ddd; }
|
||||
.notif.error { background-color: #faa; }
|
||||
.notif p { margin: 5px; }
|
||||
form.box { border: 2px solid grey; padding: 0.5em; margin: 0.5em; display: inline-block; }
|
||||
form h3 { margin: 5px; }
|
||||
]]
|
||||
|
||||
-- local script = io.open("main.js"):read("a")
|
||||
local script = [[
|
||||
|
||||
document.addEventListener("keypress", ev => {
|
||||
if (!(document.activeElement instanceof HTMLInputElement)) {
|
||||
if (ev.code.startsWith("Digit"))
|
||||
document.forms.buy_product.product.value += ev.code.substring(5)
|
||||
if (ev.code == "Enter")
|
||||
document.forms.buy_product.submit()
|
||||
}
|
||||
})
|
||||
]]
|
||||
|
||||
local function respond(status, title, body)
|
||||
|
@ -55,6 +64,7 @@ local function respond(status, title, body)
|
|||
print("Content-Type: text/html")
|
||||
print("")
|
||||
print(string.format([[
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>%s</title>
|
||||
<meta charset="utf-8" />
|
||||
|
@ -88,6 +98,13 @@ local function form_data()
|
|||
return parse_query(io.read())
|
||||
end
|
||||
|
||||
local function format_duration(t)
|
||||
if t > 86400 then return string.format("%d days", t / 86400) end
|
||||
if t > 3600 then return string.format("%d hours", t / 3600) end
|
||||
if t > 60 then return string.format("%d minutes", t / 60) end
|
||||
return string.format("%d seconds", t)
|
||||
end
|
||||
|
||||
local function read_log()
|
||||
local log = io.open("log", "r")
|
||||
if log == nil then
|
||||
|
@ -126,54 +143,65 @@ local function balances()
|
|||
end
|
||||
return users
|
||||
end
|
||||
local function last_txns()
|
||||
local users = {}
|
||||
for time, username, _, _ in read_log() do
|
||||
users[username] = time
|
||||
end
|
||||
return users
|
||||
end
|
||||
|
||||
local function r_user()
|
||||
if path == nil then
|
||||
return respond_error("no path")
|
||||
end
|
||||
local username = urldecode(path:sub(2))
|
||||
if username == nil or username:match("^([%w_ -]+)$") == nil then
|
||||
return respond_error("username invalid")
|
||||
end
|
||||
local notif = nil
|
||||
if method == "POST" then
|
||||
local data = form_data()
|
||||
local amount = nil
|
||||
local comment = ""
|
||||
if data.barcode then
|
||||
for p_barcode, p_amount, p_name in read_products() do
|
||||
if p_barcode == data.barcode then
|
||||
amount = p_amount
|
||||
comment = p_name
|
||||
end
|
||||
local function error_box(message)
|
||||
return string.format([[<div class="notif error"><p>Error: %s</p></div>]], message)
|
||||
end
|
||||
|
||||
local function r_user_post(username)
|
||||
local data = form_data()
|
||||
local amount = nil
|
||||
local comment = ""
|
||||
if data.product then
|
||||
for p_barcode, p_amount, p_name in read_products() do
|
||||
if p_barcode == data.product then
|
||||
amount = p_amount
|
||||
comment = p_name
|
||||
end
|
||||
else
|
||||
amount = tonumber(data.amount)
|
||||
comment = data.comment or ""
|
||||
end
|
||||
if amount == nil then
|
||||
return respond_error("amount invalid")
|
||||
return error_box("unknown product")
|
||||
end
|
||||
if comment:match("^[%w_ -]*$") == nil then
|
||||
return respond_error("comment invalid")
|
||||
end
|
||||
local log = io.open("log", "a+")
|
||||
if log == nil then
|
||||
return respond_error("failed to open log")
|
||||
end
|
||||
local time = os.time()
|
||||
log:write(string.format("%d,%s,%d,%s\n", time, username, amount, comment))
|
||||
log:flush()
|
||||
log:close()
|
||||
notif = string.format(
|
||||
"<div class=\"notif\"><p>Transaction successful: <strong class=\"amount-%s\">%.02f€</strong> (%s)</p></div>",
|
||||
amount >= 0 and "pos" or "neg", amount / 100, escape(comment)
|
||||
)
|
||||
else
|
||||
amount = tonumber(data.amount)
|
||||
comment = data.comment or ""
|
||||
end
|
||||
if amount == nil then
|
||||
return error_box("amount invalid")
|
||||
end
|
||||
if comment:match("^[%w_ -]*$") == nil then
|
||||
return error_box("comment invalid")
|
||||
end
|
||||
local log = io.open("log", "a+")
|
||||
if log == nil then
|
||||
return error_box("failed to open log")
|
||||
end
|
||||
local time = os.time()
|
||||
log:write(string.format("%d,%s,%d,%s\n", time, username, amount, comment))
|
||||
log:flush()
|
||||
log:close()
|
||||
return string.format(
|
||||
"<div class=\"notif\"><p>Transaction successful: <strong class=\"amount-%s\">%.02f€</strong> (%s)</p></div>",
|
||||
amount >= 0 and "pos" or "neg", amount / 100, escape(comment)
|
||||
)
|
||||
end
|
||||
|
||||
local function r_user(username)
|
||||
local notif = nil
|
||||
if method == "POST" then
|
||||
notif = r_user_post(username)
|
||||
end
|
||||
return respond(200, username, function()
|
||||
print(string.format("<h1>%s</h1>", username))
|
||||
local balance = balances()[username]
|
||||
local last_txn = last_txns()[username]
|
||||
local new_user = balance == nil
|
||||
balance = balance or 0
|
||||
if new_user then
|
||||
|
@ -181,11 +209,13 @@ local function r_user()
|
|||
<div class="notif"><p><i>This user account does not exist yet. It will only be created after the first transaction.</i></p></div>
|
||||
]])
|
||||
end
|
||||
if notif then
|
||||
print(notif)
|
||||
end
|
||||
print(string.format("<p>Current balance: <span class=\"amount-%s\">%.02f€</p>", balance >= 0 and "pos" or "neg",
|
||||
balance / 100))
|
||||
if notif then print(notif) end
|
||||
print(string.format([[
|
||||
<p>Current balance: <span class="amount-%s">%.02f€</p>
|
||||
]], balance >= 0 and "pos" or "neg", balance / 100))
|
||||
print(string.format([[
|
||||
<p>Last transaction added %s ago. <a href="/%s?log">View user log</a>
|
||||
]], format_duration(os.time() - last_txn), username))
|
||||
print([[
|
||||
<form class="transaction box" action="" method="POST">
|
||||
<h3>Create Transaction</h3>
|
||||
|
@ -195,6 +225,12 @@ local function r_user()
|
|||
<input type="text" name="comment" id="comment" /><br/>
|
||||
<input type="submit" value="Update" />
|
||||
</form>
|
||||
<form class="transaction box" action="" method="POST" id="buy_product">
|
||||
<h3>Buy Product</h3>
|
||||
<label for="product">Product: </label>
|
||||
<input type="text" name="product" id="product" /><br/>
|
||||
<input type="submit" value="Buy" />
|
||||
</form>
|
||||
]])
|
||||
print("<div class=\"amount-presets\">")
|
||||
for _, type in ipairs({ 1, -1 }) do
|
||||
|
@ -214,14 +250,20 @@ local function r_user()
|
|||
end)
|
||||
end
|
||||
|
||||
local function r_log()
|
||||
local function r_log(filter)
|
||||
return respond(200, "Log", function()
|
||||
print("<table>")
|
||||
print("<tr><th>Time</th><th>Username</th><th>Amount</th><th>Comment</th></tr>")
|
||||
for time, username, amount, comment in read_log() do
|
||||
print(string.format("<tr><td>%d</td><td>%s</td><td class=\"amount-%s\">%.02f€</td><td>%s</td></tr>", time,
|
||||
escape(username),
|
||||
amount >= 0 and "pos" or "neg", amount / 100, escape(comment)))
|
||||
if filter == nil or filter == username then
|
||||
print(string.format(
|
||||
"<tr><td>%d (%s ago)</td><td>%s</td><td class=\"amount-%s\">%.02f€</td><td>%s</td></tr>",
|
||||
time, format_duration(os.time() - time),
|
||||
escape(username),
|
||||
amount >= 0 and "pos" or "neg", amount / 100,
|
||||
escape(comment)
|
||||
))
|
||||
end
|
||||
end
|
||||
print("</table>")
|
||||
end)
|
||||
|
@ -254,6 +296,17 @@ local function r_create_user()
|
|||
return redirect(string.format("/%s", urlencode(username)))
|
||||
end
|
||||
|
||||
local function extract_username()
|
||||
if path == nil then
|
||||
return respond_error("no path")
|
||||
end
|
||||
local username = urldecode(path:sub(2))
|
||||
if username == nil or username:match("^([%w_ -]+)$") == nil then
|
||||
return nil
|
||||
end
|
||||
return username
|
||||
end
|
||||
|
||||
if path == "/" then
|
||||
if query.log then
|
||||
return r_log()
|
||||
|
@ -263,5 +316,12 @@ if path == "/" then
|
|||
return r_index()
|
||||
end
|
||||
else
|
||||
return r_user()
|
||||
local username = extract_username()
|
||||
if username == nil then
|
||||
return respond_error("username invalid")
|
||||
elseif query.log then
|
||||
return r_log(username)
|
||||
else
|
||||
return r_user(username)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue