This commit is contained in:
@@ -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.
|
||||
|
||||
50
db.go
50
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user