It has been in the repository since the initial commit carrying the live database host, user and password. Removing it from the index stops that getting worse; the credentials are still in history and should be rotated, which needs coordinating with everything that reads them. Deployments should pass configuration as container environment rather than shipping a file — a file on disk is one `git add -f` away from being committed again. Also closes the last untested path: a shopper registration published over the broker rather than posted over HTTP. All three MQTT topics — order, customer and health — have now been fired against the live Mosquitto instance and acknowledged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
123 lines
4.3 KiB
Go
123 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// cleanup removes everything the end-to-end proof wrote to the live database.
|
|
//
|
|
// Three things, in an order that leaves nothing half-undone: the stock the test
|
|
// bills consumed is returned, the bills themselves are deleted, and the price
|
|
// that was set to make a product sellable goes back to what it was.
|
|
//
|
|
// The two test bills are named explicitly rather than deleted by date or by
|
|
// "everything in pos_orders" — a table that will hold real takings tomorrow is
|
|
// not one to run an unbounded DELETE against.
|
|
// Every mobile the probes registered, so a cleanup run leaves nothing behind.
|
|
var probeMobiles = []string{"9840012345", "9840099999", "9840077777"}
|
|
|
|
var testOrderIDs = []string{
|
|
"11111111-2222-4333-8444-555555555555", // the HTTP probe
|
|
"99999999-8888-4777-8666-555555555555", // the MQTT probe
|
|
}
|
|
|
|
func cleanup(db *gorm.DB) {
|
|
var billIDs []int
|
|
db.Raw(`SELECT posorderid FROM pos_orders WHERE terminalorderid IN ?`,
|
|
testOrderIDs).Scan(&billIDs)
|
|
|
|
if len(billIDs) == 0 {
|
|
fmt.Println("no test bills found — nothing to undo")
|
|
}
|
|
|
|
// Stock first. Deleting the bills before returning what they consumed would
|
|
// leave the ledger short with nothing left to explain why.
|
|
var consumed []struct {
|
|
Productid int
|
|
Quantity float64
|
|
}
|
|
db.Raw(`SELECT productid, SUM(quantity) AS quantity
|
|
FROM pos_order_items WHERE posorderid IN ?
|
|
GROUP BY productid`, billIDs).Scan(&consumed)
|
|
|
|
for _, c := range consumed {
|
|
qty := int(c.Quantity)
|
|
if float64(qty) < c.Quantity {
|
|
qty++ // the ingest rounded up, so the reversal must too
|
|
}
|
|
if err := db.Exec(`
|
|
INSERT INTO productstocks (tenantid, stockdate, locationid, productid,
|
|
quantity, stocktype, status)
|
|
VALUES (?, NOW(), ?, ?, ?, 'in', 'Active')`,
|
|
tenantID, locationID, c.Productid, qty).Error; err != nil {
|
|
fmt.Println(" return stock:", err)
|
|
continue
|
|
}
|
|
fmt.Printf(" returned %d units of product %d\n", qty, c.Productid)
|
|
}
|
|
|
|
if len(billIDs) > 0 {
|
|
db.Exec(`DELETE FROM pos_order_items WHERE posorderid IN ?`, billIDs)
|
|
db.Exec(`DELETE FROM pos_orders WHERE posorderid IN ?`, billIDs)
|
|
fmt.Printf(" deleted %d test bill(s)\n", len(billIDs))
|
|
}
|
|
|
|
db.Exec(`UPDATE productlocations SET price = 0
|
|
WHERE tenantid=? AND locationid=? AND productid=?`,
|
|
tenantID, locationID, productID)
|
|
fmt.Printf(" product %d price restored to 0\n", productID)
|
|
|
|
// The shopper the probes created. Removed only when nothing references it —
|
|
// a customer row attached to a real order is not test data any more.
|
|
for _, mobile := range probeMobiles {
|
|
// A customer row attached to a real order is not test data any more.
|
|
var referenced int
|
|
db.Raw(`SELECT COUNT(*) FROM orders WHERE customerid IN
|
|
(SELECT customerid FROM customers WHERE contactno = ?)`,
|
|
mobile).Scan(&referenced)
|
|
if referenced > 0 {
|
|
fmt.Printf(" customer %s left in place — %d order(s) reference it\n",
|
|
mobile, referenced)
|
|
continue
|
|
}
|
|
res := db.Exec(`DELETE FROM customers WHERE contactno = ?`, mobile)
|
|
if res.RowsAffected > 0 {
|
|
fmt.Printf(" removed probe customer %s\n", mobile)
|
|
}
|
|
}
|
|
|
|
var balance float64
|
|
db.Raw(`SELECT COALESCE(
|
|
SUM(CASE WHEN LOWER(stocktype)='in' THEN quantity ELSE 0 END) -
|
|
SUM(CASE WHEN LOWER(stocktype)='out' THEN quantity ELSE 0 END), 0)
|
|
FROM productstocks WHERE tenantid=? AND locationid=? AND productid=?`,
|
|
tenantID, locationID, productID).Scan(&balance)
|
|
fmt.Printf(" product %d balance back to %.0f\n", productID, balance)
|
|
}
|
|
|
|
// showCustomer reports whether the registration probe reached the customers
|
|
// table, and under which id — the question an ack alone cannot answer.
|
|
func showCustomer(db *gorm.DB, mobile string) {
|
|
var rows []struct {
|
|
Customerid int
|
|
Firstname string
|
|
Contactno string
|
|
Applocationid int
|
|
}
|
|
db.Raw(`SELECT customerid, COALESCE(firstname,'') AS firstname,
|
|
COALESCE(contactno,'') AS contactno,
|
|
COALESCE(applocationid,0) AS applocationid
|
|
FROM customers WHERE contactno = ?`, mobile).Scan(&rows)
|
|
|
|
if len(rows) == 0 {
|
|
fmt.Printf(" no customer with contactno %s\n", mobile)
|
|
return
|
|
}
|
|
for _, r := range rows {
|
|
fmt.Printf(" customerid=%d %q contactno=%s applocid=%d\n",
|
|
r.Customerid, r.Firstname, r.Contactno, r.Applocationid)
|
|
}
|
|
}
|