Pre-rolls, edibles, and vaporizers are sold and reasoned about per unit
("$10 each") rather than as a bag total. The Add and Edit forms now ask
for "Price per unit" when the kind is discrete, and the product drawer
displays the per-unit number with the total as a small subline. Bulk
products (flower, concentrate, tincture) still take and show a total.
The stored price column remains the total, so existing data, spend
totals, sort-by-price, and bin value calculations all keep working
unchanged. Conversion (pricePerUnit × countOriginal) happens at the
form boundary on save and the inverse on edit-modal load.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A 3ct pre-roll was filling one slot of its bin instead of three. Bins
visualise physical capacity, so each unit of a discrete product (rolls,
edibles, vapes) should take its own slot; bulk jars still take one slot
each. The slot tally and fill bar both use the current count
(countLastAudit ?? countOriginal) so the bin frees up as units are
audited away.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds PATCH /products/:id and an EditProductFlow modal opened from the
product drawer. Editable fields cover name, brand, shop, bin, asset tag,
price, purchase date, size (weight or count + unit weight), and the
cannabinoid profile. SKU, type, kind, and status-derived dates stay
locked because changing them would invalidate audit history math; type
changes are surfaced as "mark gone, add new" in the modal.
The strain row is re-resolved on name or brand change so analytics stay
aligned, and the last-audit mirror (last_audit_weight / count_last_audit)
only syncs with the original size when there are no audits yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds PATCH and DELETE endpoints for brands and shops that mirror the
existing bins pattern: deleting a brand or shop nullifies referencing
products (and strains, for brands) inside a transaction so nothing is
lost. Brand renames return 409 when the new name collides with the
UNIQUE constraint, surfaced inline in the edit modal.
The Brands and Shops views now show inline edit/trash icons on each
card; the trash button confirms with a preview of how many products
will be unparented.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>