feat(review): S5 pricing rules + deterministic title/short desc + server-derived ready + Review UI

- backend/pricing.py: /admin/pricing CRUD (one flat $/bdft per species leaf, SETTINGS-35).
- backend/review.py: POST /slabs/{id}/review -> deterministic title + short description
  (7 leaf patterns, length ft/in split, thickness 1/8-in precision, METADATA-80/85/90),
  price suggestion (bdft x species rate, REVIEW-25), server-derived ready + missing set
  (REVIEW-5).
- frontend: create/review/Review.svelte (slab id, primary category, price override,
  ready state + generated copy) + dev toggle.
- tests: 3 pricing + 4 review tests (title no-dims, ready set, price suggestion,
  knife-blank dims).

Verified: pytest 43 passed; gates exit 0; npm build ok.
This commit is contained in:
2026-09-19 16:52:33 -07:00
parent 861c5e0dd1
commit 732310fdbe
7 changed files with 540 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
"""S5 pricing rules tests (SETTINGS-35, REVIEW-25)."""
import base64
import os
import shutil
import tempfile
import pytest
from fastapi.testclient import TestClient
import backend.db as db_mod
@pytest.fixture
def env():
tmp = tempfile.mkdtemp(prefix="pricing_")
os.environ["SLAB_DB_PATH"] = os.path.join(tmp, "p.db")
os.environ["SLAB_IMAGES_DIR"] = tmp
os.environ["WOO_BASE_URL"] = "https://example.com"
os.environ["SLAB_AES_KEY"] = base64.b64encode(b"0123456789abcdef0123456789abcdef").decode()
db_mod._reset_for_tests()
from backend.app import app
conn = db_mod.init_db()
for cid in (1, 7):
conn.execute("INSERT OR IGNORE INTO woo_categories(id,name,slug,parent_id,woo_id) VALUES (?,?,?,NULL,?)", (cid, f"Sp{cid}", f"sp{cid}", cid))
conn.commit()
yield TestClient(app)
db_mod._reset_for_tests()
shutil.rmtree(tmp, ignore_errors=True)
def test_crud_cycle(env):
r = env.post("/api/v1/admin/pricing", json={"species_id": 1, "price_per_bdft": 12.5})
assert r.status_code == 201, r.text
rule = r.json()
rid = rule["id"]
assert rule["price_per_bdft"] == 12.5
r = env.get("/api/v1/admin/pricing")
assert r.status_code == 200
assert [x["species_id"] for x in r.json()] == [1]
r = env.put(f"/api/v1/admin/pricing/{rid}", json={"price_per_bdft": 15.0})
assert r.status_code == 200
assert r.json()["price_per_bdft"] == 15.0
r = env.delete(f"/api/v1/admin/pricing/{rid}")
assert r.status_code == 204
assert env.get("/api/v1/admin/pricing").json() == []
def test_duplicate_species(env):
env.post("/api/v1/admin/pricing", json={"species_id": 7, "price_per_bdft": 1.0})
r = env.post("/api/v1/admin/pricing", json={"species_id": 7, "price_per_bdft": 2.0})
assert r.status_code == 409
def test_validation(env):
r = env.post("/api/v1/admin/pricing", json={"species_id": 1})
assert r.status_code == 400
r = env.post("/api/v1/admin/pricing", json={"species_id": 1, "price_per_bdft": -5})
assert r.status_code == 400