diff --git a/README.md b/README.md
index fb3784c..85de2ac 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ inventory_summary()
| | |
|---|---|
| **Tools** | `inventory_summary` · `inventory_list` · `inventory_get` · `inventory_upsert_lot` · `inventory_upsert_item` · `inventory_delete_item` · `inventory_set_price` · `inventory_mark_sold` · `inventory_listing` · `inventory_import_csv` · `inventory_export_csv` · `inventory_reprice_plan` · `inventory_stale` |
-| **View** | a rail panel: the item grid (double-click to edit name/category/condition/qty; status select; click a price for the Price dialog), Price / Sold / Listing / Edit / Delete per row, an optional **game system** per item (filter, ordering, inline edit with suggestions), multi-select with **Copy as Markdown** grouped `## system` → `### lot` → `- Name — Category — $price` (also ⌘C), lots with their P&L, sales, an activity log, CSV import (file or paste, with the mapping report) and export |
+| **View** | a rail panel: the item grid (double-click to edit name/category/condition/qty; status select; click a price for the Price dialog), Price / Sold / Listing / Edit / Delete per row, an optional **game system** per item (filter, ordering, inline edit with suggestions), multi-select with **Copy list** (also ⌘C) — a for-sale post per game system: the system as a header, `(all NoS)` when the group shares a condition, `Name (condition) — $price` per line, pending/listed items under their own marker, lots with their P&L, sales, an activity log, CSV import (file or paste, with the mapping report) and export |
| **API** | bearer-gated JSON under `/api/plugins/inventory` — `summary`, `lots`, `items`, `items/{id}/price`, `items/{id}/sold`, `items/{id}/listings`, `listings/{id}/end`, `sales`, `stale`, `audit`, `import`, `export` |
| **Automations** | `weekly_review: true` arms a plugin-owned recurring turn (`weekly_review_cron`, default Monday 09:00 in `review_timezone`) that re-prices stale evidence from eBay sold comps through the same tools, reports stale listings with a recommendation (it never changes a listing itself), and posts the per-lot P&L. Cancelled when the plugin is disabled. |
| **Events** | `inventory.item.changed`, `inventory.lot.changed`, `inventory.sale.recorded`, `inventory.imported` |
diff --git a/api.py b/api.py
index 8400813..242fa0d 100644
--- a/api.py
+++ b/api.py
@@ -40,6 +40,10 @@ async def _lots() -> dict:
async def _systems() -> dict:
return {"systems": store.systems()}
+ @r.get("/conditions")
+ async def _conditions() -> dict:
+ return {"conditions": store.conditions()}
+
@r.put("/lots/{lot_id}")
async def _put_lot(lot_id: str, body: dict) -> dict:
try:
diff --git a/protoagent.plugin.yaml b/protoagent.plugin.yaml
index ded8f90..46903e7 100644
--- a/protoagent.plugin.yaml
+++ b/protoagent.plugin.yaml
@@ -1,6 +1,6 @@
id: inventory
name: Inventory (resale source of truth)
-version: 0.4.0
+version: 0.4.1
description: >-
The resale inventory as a source of truth: lots (what you bought and for how much),
items (what each lot split into, its condition and status), listings (where each item is
diff --git a/pyproject.toml b/pyproject.toml
index 1824a8f..7887139 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "inventory-plugin"
-version = "0.4.0"
+version = "0.4.1"
description = "Resale inventory as a source of truth for protoAgent — lots, items, listings, sales, price evidence, audit"
requires-python = ">=3.11"
diff --git a/store.py b/store.py
index 9a945e0..f8fe086 100644
--- a/store.py
+++ b/store.py
@@ -534,6 +534,12 @@ def systems(self) -> list[str]:
rows = con.execute("SELECT DISTINCT system FROM items WHERE system!='' ORDER BY system").fetchall()
return [r[0] for r in rows]
+ def conditions(self) -> list[str]:
+ """The distinct condition labels in use (NoS, NIB, sealed, painted …) for suggestions."""
+ with self._read() as con:
+ rows = con.execute("SELECT DISTINCT condition FROM items WHERE condition!='' ORDER BY condition").fetchall()
+ return [r[0] for r in rows]
+
def find_item(self, *, lot_id: str, name: str) -> dict | None:
"""The item with this lot + name (case-insensitive) — how a sheet without an id column
is matched on re-import instead of minting duplicates."""
diff --git a/tests/test_system.py b/tests/test_system.py
index 5569274..0778539 100644
--- a/tests/test_system.py
+++ b/tests/test_system.py
@@ -63,6 +63,9 @@ def test_api_lists_systems_and_filters(registry):
c.post("/api/plugins/inventory/items", json={"id": "B", "name": "b", "system": "Warhammer 40K"})
c.post("/api/plugins/inventory/items", json={"id": "C", "name": "c"})
assert c.get("/api/plugins/inventory/systems").json() == {"systems": ["Blood Bowl", "Warhammer 40K"]}
+ c.put("/api/plugins/inventory/items/A", json={"condition": "NoS"})
+ c.put("/api/plugins/inventory/items/B", json={"condition": "NIB"})
+ assert c.get("/api/plugins/inventory/conditions").json() == {"conditions": ["NIB", "NoS"]}
assert [
i["id"] for i in c.get("/api/plugins/inventory/items", params={"system": "Blood Bowl"}).json()["items"]
] == ["A"]
diff --git a/tests/test_view.py b/tests/test_view.py
index cbb5ff8..e1cfab9 100644
--- a/tests/test_view.py
+++ b/tests/test_view.py
@@ -110,9 +110,10 @@ def test_the_pure_helpers_under_node():
"\nconsole.log(JSON.stringify({ neg: fmt(-12.5), pos: fmt(1234.5), nul: fmt(null), zero: fmt(0), sneg: signed(-3), spos: signed(3), snul: signed(null),"
" esc: esc('&\\''), field: field('name', 'Name', '\">
'), opt: field('lot_id', 'Lot', '', { type: 'select', options: [['\">', 'x