SETTINGS-35: species $/bdft in Settings + taxonomy parent fix (#76)

Add Settings species pricing UI (children of Species grouper by slug,
never hardcoded ids). Map Woo REST parent→parent_id on taxonomy sync;
upsert categories so slab FKs survive re-sync. Tests: parent map, FK-safe
sync, TV-8 0.96×12.50→12.00, Playwright mock e2e. UAT verified on 5060.
This commit is contained in:
Ty
2026-09-20 12:37:52 -07:00
parent b99cbdf194
commit 18c262b5b4
7 changed files with 655 additions and 5 deletions
+41
View File
@@ -87,6 +87,47 @@ def test_price_suggestion_from_rate(env):
assert body["price"] == 184.0
def test_price_suggestion_tv8_quality_90(env):
"""QUALITY-90 / TV-8: 0.96 bdft × $12.50 → 12.00."""
client, conn = env
conn.execute(
"INSERT INTO pricing_rules(species_id,price_per_bdft,created_at,updated_at) VALUES (1,12.50,'t','t')"
)
conn.execute(
"INSERT INTO slabs(id,sku,length_in,thickness_in,sqft,bdft,width_min_in,width_max_in,width_avg_in,species_id,wood_category_ids,figure_term_ids,price,price_source,title,short_description,description,content_source,status,server_rev,client_rev,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
"s1",
"SLAB-01",
114.0,
1.75,
30.0,
0.96,
18.0,
24.3,
24.3,
1,
json.dumps([2]),
json.dumps([1]),
None,
None,
None,
None,
"A slab",
"manual",
"calibrated",
1,
1,
"t",
"t",
),
)
conn.commit()
r = client.post("/api/v1/slabs/s1/review", json={})
body = r.json()
assert body["price_suggestion"] == 12.00
assert body["price"] == 12.00
def test_knife_blank_carries_dims(env):
client, conn = env
conn.execute("INSERT INTO woo_categories(id,name,slug,parent_id,woo_id) VALUES (3,'Knife blanks','knife-blanks',NULL,102)")
+69
View File
@@ -141,6 +141,75 @@ def test_anchor_stable_across_identical_syncs(db_dir):
assert r1["taxonomy_anchor"] == r2["taxonomy_anchor"]
def test_sync_maps_woo_parent_field_to_parent_id(db_dir):
"""Woo REST categories use `parent` (0 = top-level), not `parent_id`.
Live UAT lost hierarchy when sync only read parent_id (SETTINGS-35 / METADATA-10).
Fixture ids are local to this test — not production store ids.
"""
cats = (
200,
{"X-WP-TotalPages": "1"},
json.dumps(
[
{"id": 10, "name": "Species", "slug": "species", "parent": 0},
{"id": 11, "name": "Cedar", "slug": "cedar", "parent": 10},
{"id": 20, "name": "Wood category", "slug": "wcat", "parent": 0},
{"id": 21, "name": "Live edge wood", "slug": "liveedge", "parent": 20},
]
).encode(),
)
tags = (200, {"X-WP-TotalPages": "1"}, b"[]")
attrs = (200, {"X-WP-TotalPages": "1"}, b"[]")
c, _ = _client([cats, tags, attrs])
taxonomy_mod.sync(c, db_mod.get_connection())
conn = db_mod.get_connection()
rows = {
r[0]: {"name": r[1], "slug": r[2], "parent_id": r[3], "woo_id": r[4]}
for r in conn.execute("SELECT id, name, slug, parent_id, woo_id FROM woo_categories")
}
assert rows[10]["parent_id"] is None
assert rows[11]["parent_id"] == 10
assert rows[20]["parent_id"] is None
assert rows[21]["parent_id"] == 20
# woo_id falls back to Woo category id when the payload has no woo_id field
assert rows[11]["woo_id"] == 11
def test_sync_with_slab_species_fk_does_not_fail(db_dir):
"""Live UAT: DELETE woo_categories fails when a slab still points at species_id."""
conn = db_mod.get_connection()
conn.execute(
"INSERT INTO woo_categories(id,name,slug,parent_id,woo_id) VALUES (11,'Old Cedar','old-cedar',NULL,11)"
)
conn.execute(
"INSERT INTO slabs(id, status, species_id, server_rev, created_at, updated_at) "
"VALUES ('slab-fk-1', 'draft', 11, 1, 't', 't')"
)
conn.commit()
cats = (
200,
{"X-WP-TotalPages": "1"},
json.dumps(
[
{"id": 10, "name": "Species", "slug": "species", "parent": 0},
{"id": 11, "name": "Cedar", "slug": "cedar", "parent": 10},
]
).encode(),
)
tags = (200, {"X-WP-TotalPages": "1"}, b"[]")
attrs = (200, {"X-WP-TotalPages": "1"}, b"[]")
c, _ = _client([cats, tags, attrs])
taxonomy_mod.sync(c, conn)
rows = {
r[0]: r[3]
for r in conn.execute("SELECT id, name, slug, parent_id FROM woo_categories")
}
assert rows[11] == 10 # parent mapped; row kept for FK
assert conn.execute("SELECT species_id FROM slabs WHERE id='slab-fk-1'").fetchone()[0] == 11
def test_403_raises_forbidden_error(db_dir):
def fake_request(url, method, data, headers):
raise WooForbiddenError("request forbidden (403)")