Backend: Einzelstücke (Items) mit UID je Gegenstand
Gegenstands-Produkte koennen als Einzelstuecke gefuehrt werden (Product.individual):
jedes physische Stueck ist ein Item mit eigener kurzer UID (fuer QR), eigenem
Lagerort, Kaufdatum, Garantie, Bezugsquelle und Notiz. So laesst sich dasselbe
Modell mehrfach getrennt fuehren (Powerbank 2024 + 2025).
Neu: models.Item + Product.individual (+Migration), Schemas, services/items.py
(UID-Erzeugung, Anreicherung), routers/items.py (CRUD, /items/by-uid/{uid} zur
QR-Aufloesung, /items/{id}/remove mit Grund als Bewegung). current_stock zaehlt
bei Einzelstueck-Produkten die Items. Tests + HTTP-Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from app.models import (
|
||||
Category,
|
||||
CategoryTracking,
|
||||
FieldDefinition,
|
||||
Item,
|
||||
Location,
|
||||
Lot,
|
||||
Movement,
|
||||
@@ -21,6 +22,7 @@ from app.models import (
|
||||
ProductFieldValue,
|
||||
RemovalReason,
|
||||
)
|
||||
from app.services.items import generate_uid
|
||||
from app.routers.categories import create_category
|
||||
from app.schemas import CategoryCreate
|
||||
from app.services.fields import (
|
||||
@@ -236,3 +238,40 @@ def test_feldwert_setzen_und_leeren(db):
|
||||
apply_field_values(db, product, {fd.id: ""})
|
||||
db.commit()
|
||||
assert db.query(ProductFieldValue).filter_by(product_id=product.id).count() == 0
|
||||
|
||||
|
||||
# ---- Einzelstücke (Items mit UID/QR) ----
|
||||
|
||||
def _individual_product(db, name="Powerbank"):
|
||||
cat = Category(name="Elektronik", tracking=CategoryTracking.object.value)
|
||||
db.add(cat)
|
||||
db.flush()
|
||||
product = Product(name=name, category_id=cat.id, individual=True)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(product)
|
||||
return product
|
||||
|
||||
|
||||
def test_uid_ist_eindeutig(db):
|
||||
codes = {generate_uid(db) for _ in range(50)}
|
||||
assert len(codes) == 50
|
||||
# Keine verwechselbaren Zeichen (0/O/1/I/L) in der UID.
|
||||
assert all(ch not in "01OIL" for code in codes for ch in code)
|
||||
|
||||
|
||||
def test_einzelstueck_bestand_zaehlt_items(db):
|
||||
product = _individual_product(db)
|
||||
for _ in range(3):
|
||||
db.add(Item(uid=generate_uid(db), product_id=product.id))
|
||||
db.commit()
|
||||
assert current_stock(db, product.id) == 3
|
||||
|
||||
|
||||
def test_mengen_produkt_zaehlt_weiter_lots(db):
|
||||
# Ein Nicht-Einzelstück-Produkt zählt weiterhin über Lots, nicht über Items.
|
||||
product, _ = _object_product(db, name="Unterhose")
|
||||
a = _loc(db, "Schrank A")
|
||||
object_add(db, product, 5, a.id, None)
|
||||
db.commit()
|
||||
assert current_stock(db, product.id) == 5
|
||||
|
||||
Reference in New Issue
Block a user