From bcc75dc571056b655aeb54d60556e8fb7896102e Mon Sep 17 00:00:00 2001 From: jbergner Date: Fri, 29 May 2026 11:22:51 +0200 Subject: [PATCH] =?UTF-8?q?l=C3=B6sche=20leere=20Lager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++++ db.go | 50 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8aac3e9..c6056b4 100644 --- a/README.md +++ b/README.md @@ -172,3 +172,11 @@ SQLite wird mit WAL-Modus und `busy_timeout` betrieben, damit kleine produktive - Führe den Bot als systemd-Service oder Container aus. - Aktiviere Monitoring für Logs und Neustarts. - Teste DMs: Manche Nutzer blockieren DMs von Servermitgliedern; der Bot loggt solche Fehler, kann sie aber nicht erzwingen. + +## Lagerbestand bei 0 + +Wenn ein Lagerbestand durch `/lager_remove` oder `/lager_add` mit `modus:setzen` auf `0` fällt, wird der Lagerdatensatz automatisch gelöscht. Das Auditlog zeigt weiterhin die Änderung als `alter Wert → neuer Wert`, z. B. `2 → 0`. + +Zusätzlich verhindert die Lagerlogik negative Bestände. Wenn mehr entfernt wird als vorhanden ist, antwortet der Bot mit einem Fehler und ändert den Bestand nicht. + +`/lager_liste` und die interne Lagerprüfung berücksichtigen nur Bestände größer als `0`, sodass alte Nullbestände nicht mehr angezeigt werden. diff --git a/db.go b/db.go index 8574242..b728f94 100644 --- a/db.go +++ b/db.go @@ -309,19 +309,36 @@ func nullableOrderID(id int64) any { func (s *Store) UpsertInventory(item InventoryItem, delta bool) (*InventoryItem, error) { now := time.Now().UTC() + item.Name = strings.TrimSpace(item.Name) + item.Location = strings.TrimSpace(item.Location) item.NormalizedName = normalizeName(item.Name) + tx, err := s.db.Begin() if err != nil { return nil, err } defer tx.Rollback() + var existing InventoryItem err = tx.QueryRow(`SELECT id, name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at FROM inventory WHERE normalized_name=? AND quality=? AND quantity_unit=? AND location=?`, item.NormalizedName, item.Quality, item.QuantityUnit, item.Location).Scan(&existing.ID, &existing.Name, &existing.NormalizedName, &existing.Quality, &existing.QuantityAmount, &existing.QuantityUnit, &existing.Location, &existing.Note, &existing.UpdatedByID, &existing.UpdatedByName, &existing.CreatedAt, &existing.UpdatedAt) if err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, err } + + var result InventoryItem if errors.Is(err, sql.ErrNoRows) { - _, err = tx.Exec(`INSERT INTO inventory(name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, item.Name, item.NormalizedName, item.Quality, item.QuantityAmount, item.QuantityUnit, item.Location, item.Note, item.UpdatedByID, item.UpdatedByName, now, now) + if item.QuantityAmount <= 0 { + return nil, errors.New("kein ausreichender Lagerbestand vorhanden") + } + res, err := tx.Exec(`INSERT INTO inventory(name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, item.Name, item.NormalizedName, item.Quality, item.QuantityAmount, item.QuantityUnit, item.Location, item.Note, item.UpdatedByID, item.UpdatedByName, now, now) + if err != nil { + return nil, err + } + id, _ := res.LastInsertId() + result = item + result.ID = id + result.CreatedAt = now + result.UpdatedAt = now } else { newQty := item.QuantityAmount if delta { @@ -330,18 +347,33 @@ func (s *Store) UpsertInventory(item InventoryItem, delta bool) (*InventoryItem, if newQty < 0 { return nil, errors.New("lagerbestand darf nicht negativ werden") } - _, err = tx.Exec(`UPDATE inventory SET name=?, quantity_amount=?, note=?, updated_by_id=?, updated_by_name=?, updated_at=? WHERE id=?`, item.Name, newQty, item.Note, item.UpdatedByID, item.UpdatedByName, now, existing.ID) - } - if err != nil { - return nil, err + + result = existing + result.Name = item.Name + result.QuantityAmount = newQty + result.Note = item.Note + result.UpdatedByID = item.UpdatedByID + result.UpdatedByName = item.UpdatedByName + result.UpdatedAt = now + + if newQty == 0 { + if _, err := tx.Exec(`DELETE FROM inventory WHERE id=?`, existing.ID); err != nil { + return nil, err + } + } else { + if _, err := tx.Exec(`UPDATE inventory SET name=?, quantity_amount=?, note=?, updated_by_id=?, updated_by_name=?, updated_at=? WHERE id=?`, item.Name, newQty, item.Note, item.UpdatedByID, item.UpdatedByName, now, existing.ID); err != nil { + return nil, err + } + } } + if err := addEventTx(tx, 0, item.UpdatedByID, item.UpdatedByName, EventInventoryChanged, fmt.Sprintf("Lager %s Q%d %s %s", item.Name, item.Quality, formatAmount(item.QuantityAmount), item.QuantityUnit)); err != nil { return nil, err } if err := tx.Commit(); err != nil { return nil, err } - return s.GetInventoryByKey(item.NormalizedName, item.Quality, item.QuantityUnit, item.Location) + return &result, nil } func (s *Store) GetInventoryByKey(normalized string, quality int, unit, location string) (*InventoryItem, error) { @@ -361,10 +393,10 @@ func (s *Store) ListInventory(query string, limit int) ([]InventoryItem, error) if limit <= 0 || limit > 50 { limit = 20 } - q := `SELECT id, name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at FROM inventory` + q := `SELECT id, name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at FROM inventory WHERE quantity_amount > 0` args := []any{} if strings.TrimSpace(query) != "" { - q += ` WHERE normalized_name LIKE ? OR location LIKE ?` + q += ` AND (normalized_name LIKE ? OR location LIKE ?)` like := "%" + normalizeName(query) + "%" args = append(args, like, like) } @@ -387,7 +419,7 @@ func (s *Store) ListInventory(query string, limit int) ([]InventoryItem, error) } func (s *Store) FindInventoryForOrder(o *Order) ([]InventoryMatch, float64, error) { - rows, err := s.db.Query(`SELECT id, name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at FROM inventory WHERE normalized_name=? AND quality>=? AND quantity_unit=? ORDER BY quality DESC, quantity_amount DESC`, normalizeName(o.Commodity), o.Quality, o.QuantityUnit) + rows, err := s.db.Query(`SELECT id, name, normalized_name, quality, quantity_amount, quantity_unit, location, note, updated_by_id, updated_by_name, created_at, updated_at FROM inventory WHERE normalized_name=? AND quality>=? AND quantity_unit=? AND quantity_amount > 0 ORDER BY quality DESC, quantity_amount DESC`, normalizeName(o.Commodity), o.Quality, o.QuantityUnit) if err != nil { return nil, 0, err }