add support for multible barcodes per product; products file needs to be migrated manually

This commit is contained in:
Riley L. 2025-01-02 00:03:45 +01:00
parent a23cdf1ee4
commit 5be820b181
2 changed files with 160 additions and 22 deletions

View file

@ -30,6 +30,7 @@ local matchers = {
barcode = "([%w_-]+)", barcode = "([%w_-]+)",
barcode_opt = "([%w_-]*)", barcode_opt = "([%w_-]*)",
name = "([%w_ -]+)", name = "([%w_ -]+)",
name_opt = "([%w_ -]*)",
} }
local matchers_global = (function() local matchers_global = (function()
local s = {} local s = {}
@ -142,6 +143,26 @@ local function format_amount(amount, tag, classes)
}) })
end end
local function format_list(list)
if list == nil then
return nil
end
local not_first = false
local out = ""
for _, barcode in pairs(list) do
if not_first then
out = out .. ", " .. barcode
else
out = barcode
end
not_first = true
end
return out
end
local function get_user_theme(username) local function get_user_theme(username)
local c = "" local c = ""
if username == "_jeb" then if username == "_jeb" then
@ -254,9 +275,9 @@ local function read_log()
if l == "" or l == nil then if l == "" or l == nil then
return nil return nil
end end
local time, user_src, user_dst, amount, pcode, pcount, comment = string.match(l, local time, user_src, user_dst, amount, pname, pcount, comment = string.match(l,
format("^{time},{user},{user},{amount},{barcode_opt},{amount_opt},{comment_opt}$", matchers)) format("^{time},{user},{user},{amount},{name_opt},{amount_opt},{comment_opt}$", matchers))
return tonumber(time), user_src, user_dst, tonumber(amount), pcode, tonumber(pcount), comment return tonumber(time), user_src, user_dst, tonumber(amount), pname, tonumber(pcount), comment
end end
end end
@ -271,8 +292,24 @@ local function read_products()
if l == "" or l == nil then if l == "" or l == nil then
return nil return nil
end end
local barcode, price, user, name = string.match(l, format("^{barcode},{amount},{user_opt},{name}$", matchers)) local price, user, name = string.match(l, format("^{amount},{user_opt},{name}$", matchers))
return barcode, tonumber(price), user, name return tonumber(price), user, name
end
end
local function read_barcodes()
local log = io.open("barcodes", "r")
if log == nil then
return function() return nil end
end
local lines = log:lines("l")
return function()
local l = lines()
if l == "" or l == nil then
return nil
end
local name, barcode = string.match(l, format("^{name},{barcode}$", matchers))
return name, barcode
end end
end end
@ -336,10 +373,25 @@ local function r_transaction_post()
local pcode = data.pcode local pcode = data.pcode
local pcount = tonumber(data.pcount) local pcount = tonumber(data.pcount)
local comment = data.comment local comment = data.comment
local barcode_name = nil
if pcode ~= nil and pcode ~= "" then if pcode ~= nil and pcode ~= "" then
-- check if barcode exists
local exists = false
for p_name, p_barcode in read_barcodes() do
if p_barcode == pcode then
exists = true
barcode_name = p_name
end
end
if not exists then
return error_box("{+error.unknown_barcode}")
end
-- check if product exists
local exists = false local exists = false
for p_barcode, p_amount, p_user, p_name in read_products() do for p_amount, p_user, p_name in read_products() do
if p_barcode == pcode then if barcode_name == p_name then
pcount = (tonumber(data.pcount) or 1) * (data.negate_pcount ~= nil and -1 or 1) pcount = (tonumber(data.pcount) or 1) * (data.negate_pcount ~= nil and -1 or 1)
amount = amount or pcount * p_amount amount = amount or pcount * p_amount
user_src = user_src or p_user user_src = user_src or p_user
@ -349,9 +401,11 @@ local function r_transaction_post()
end end
end end
if not exists then if not exists then
return error_box("{+error.unknown_product}") return error_box("{+error.unknown_product}"..barcode_name)
end end
end end
-- for outside money
user_src = user_src or "@Potential" user_src = user_src or "@Potential"
if amount == nil then if amount == nil then
return error_box("{+error.invalid_amount}") return error_box("{+error.invalid_amount}")
@ -371,7 +425,7 @@ local function r_transaction_post()
end end
local time = os.time() local time = os.time()
log:write(string.format("%d,%s,%s,%d,%s,%s,%s\n", log:write(string.format("%d,%s,%s,%d,%s,%s,%s\n",
time, user_src, user_dst, amount, pcode or "", pcount or "", comment)) time, user_src, user_dst, amount, barcode_name or "", pcount or "", comment))
log:flush() log:flush()
log:close() log:close()
return format([[ return format([[
@ -644,23 +698,41 @@ end
local function r_products_post() local function r_products_post()
local data = form_data() local data = form_data()
local barcode = data.barcode local name = data.name
if barcode == nil or barcode:match("^[%w_-]*$") == nil then if name == nil or name:match("^[%w_-]*$") == nil then
return error_box("{+error.invalid_barcode}") return error_box("{+error.invalid_name}")
end end
if data.delete then if data.delete then
local new_products = io.open("products.new", "w+") local new_products = io.open("products.new", "w+")
if new_products == nil then if new_products == nil then
return error_box("{+error.open_new_products}") return error_box("{+error.open_new_products}")
end end
for a_barcode, price, user, name in read_products() do for price, user, a_name in read_products() do
if barcode ~= a_barcode then if name ~= a_name then
new_products:write(string.format("%s,%d,%s,%s\n", a_barcode, price, user, name)) new_products:write(string.format("%d,%s,%s\n", price, user, name))
end end
end end
new_products:flush() new_products:flush()
new_products:close() new_products:close()
os.rename("products.new", "products") os.rename("products.new", "products")
elseif data.addbarcode then
-- add barcode
local name = data.name
local barcode = data.barcode
if name == nil or name:match(matchers_global.name) == nil then
return error_box("{+error.invalid_name}")
end
if barcode == nil or barcode:match(matchers_global.barcode) == nil then
return error_box("{+error.invalid_barcode}")
end
local barcodes = io.open("barcodes", "a+")
if barcodes == nil then
return error_box("{+error.open_barcodes}")
end
barcodes:write(string.format("%s,%s\n", name, barcode))
barcodes:flush()
barcodes:close()
else else
local price = tonumber(data.price) local price = tonumber(data.price)
local name = data.name local name = data.name
@ -678,7 +750,7 @@ local function r_products_post()
if products == nil then if products == nil then
return error_box("{+error.open_products}") return error_box("{+error.open_products}")
end end
products:write(string.format("%s,%d,%s,%s\n", barcode, price, user, name)) products:write(string.format("%d,%s,%s\n", price, user, name))
products:flush() products:flush()
products:close() products:close()
end end
@ -702,6 +774,23 @@ local function r_products()
<input type="number" name="price" id="price" /> <input type="number" name="price" id="price" />
<label for="user">{+field.user}: </label> <label for="user">{+field.user}: </label>
<input type="text" name="user" id="user" /> <input type="text" name="user" id="user" />
<input type="submit" value="{+products.form.add.submit}" class="amount-ntr button" />
</form>
<form action="/?products" method="POST" class="box backgroundbox">
<h3>{+products.form.addbarcode}</h3>
<input type="text" name="addbarcode" value="1" hidden />
<label for="name">{+field.name}: </label>
<select type="text" name="name" id="name">
]], {
currency = config.unit or "",
}))
for _, _, name in read_products() do
print(format([[<option name="{name}">{name}</option>]], {
name = name,
}))
end
print(format([[</select>
<label for="barcode">{+field.barcode}: </label> <label for="barcode">{+field.barcode}: </label>
<input type="text" name="barcode" id="barcode" /> <input type="text" name="barcode" id="barcode" />
<input type="submit" value="{+products.form.add.submit}" class="amount-ntr button" /> <input type="submit" value="{+products.form.add.submit}" class="amount-ntr button" />
@ -709,8 +798,51 @@ local function r_products()
<form action="/?products" method="POST" class="box backgroundbox"> <form action="/?products" method="POST" class="box backgroundbox">
<h3>{+products.form.remove}</h3> <h3>{+products.form.remove}</h3>
<input type="text" name="delete" value="1" hidden /> <input type="text" name="delete" value="1" hidden />
<label for="name">{+field.name}: </label>
<select type="text" name="name" id="name">
]], {
currency = config.unit or "",
}))
for _, _, name in read_products() do
print(format([[<option name="{name}">{!name}</option>]], {
name = name,
}))
end
print(format([[</select>
<input type="submit" value="{+products.form.remove.submit}" class="amount-ntr button" />
</form>
<!-- remove barcode -->
<form action="/?products" method="POST" class="box backgroundbox">
<h3>{+products.form.removebarcode}</h3>
<input type="text" name="delete" value="1" hidden />
<label for="barcode">{+field.barcode}: </label> <label for="barcode">{+field.barcode}: </label>
<input type="text" name="barcode" id="barcode" /> <select type="text" name="barcode" id="barcode">
]], {
currency = config.unit or "",
}))
local barcodes = {}
for name, barcode in read_barcodes() do
print(format([[<option name="{barcode}">{!barcode} ({!name})</option>]], {
name = name,
barcode = barcode,
}))
if barcodes[name] ~= nil then
table.insert(barcodes[name], barcode)
else
barcodes[name] = {}
table.insert(barcodes[name], barcode)
end
end
for a, b in pairs(barcodes) do
print("name: " .. a)
for _, c in pairs(b) do
print("\t"..c)
end
end
print(format([[</select>
<input type="submit" value="{+products.form.remove.submit}" class="amount-ntr button" /> <input type="submit" value="{+products.form.remove.submit}" class="amount-ntr button" />
</form> </form>
</div> </div>
@ -720,26 +852,27 @@ local function r_products()
print(format([[<table class="productlist"><tr> print(format([[<table class="productlist"><tr>
<th>{+field.name}</th> <th>{+field.name}</th>
<th>{+field.price}</th> <th>{+field.price}</th>
<th>{+field.barcode}</th>
<th>{+field.count}</th> <th>{+field.count}</th>
<th>{+field.user}</th> <th>{+field.user}</th>
<th>{+field.barcode}</th>
</tr>]])) </tr>]]))
local pbals = product_balances() local pbals = product_balances()
for barcode, price, user, name in read_products() do for price, user, name in read_products() do
print(format([[<tr> print(format([[<tr>
<td>{!name}</td> <td>{!name}</td>
{price} {price}
<td>{!barcode}</td>
<td>{!count}</td> <td>{!count}</td>
<td>{!user}</td> <td>{!user}</td>
<td>{barcodes}</td>
</tr>]], { </tr>]], {
name = name, name = name,
price = format_amount(-price, "td"), price = format_amount(-price, "td"),
barcode = barcode,
count = tostring(pbals[barcode] or 0), count = tostring(pbals[barcode] or 0),
user = user, user = user,
barcodes = format_list(barcodes[name]) or "{+field.not_set}",
})) }))
end end
print("</table>") print("</table>")
end) end)
end end

View file

@ -16,14 +16,17 @@ field.time=Time
field.upstream_price=Upstream Price field.upstream_price=Upstream Price
field.user=User field.user=User
field.username=Username field.username=Username
field.not_set=Not Set
index.form.create_user.submit=Continue index.form.create_user.submit=Continue
index.form.create_user=User Creation index.form.create_user=User Creation
log.actions.revert=Revert log.actions.revert=Revert
log.actions=Actions log.actions=Actions
log=Log log=Log
products.form.add=Add Product products.form.add=Add Product
products.form.addbarcode=Add Barcode
products.form.add.submit=Add products.form.add.submit=Add
products.form.remove=Remove Product products.form.remove=Remove Product
products.form.removebarcode=Remove Barcode
products.form.remove.submit=Remove products.form.remove.submit=Remove
products.form.title=Product List products.form.title=Product List
products.title=Product List products.title=Product List
@ -55,6 +58,7 @@ users=Users
user.special=This is a special user. user.special=This is a special user.
users.filter=Filter users.filter=Filter
error.unknown_product=Unknown product error.unknown_product=Unknown product
error.unknown_barcode=Unknown barcode
error.invalid_amount=Amount invalid error.invalid_amount=Amount invalid
error.invalid_comment=Comment invalid error.invalid_comment=Comment invalid
error.invalid_user_src=Source user invalid error.invalid_user_src=Source user invalid
@ -66,6 +70,7 @@ error.open_new_products=Failed to open new products file
error.invalid_price=Price invalid error.invalid_price=Price invalid
error.invalid_name=Name invalid error.invalid_name=Name invalid
error.open_products=Failed to open products file error.open_products=Failed to open products file
error.open_barcodes=Failed to open barcodes file
error.no_path=No path error.no_path=No path
users.inactive_list=Inactive Users users.inactive_list=Inactive Users
users.embezzlement=A total of {amount} are currently under embezzlement. users.embezzlement=A total of {amount} are currently under embezzlement.