Count List (printable count sheet)
Count List — Technical Spec
app/po.py, countlist_print(), template po_countlist.html, screen key po_countlist. The template is a standalone HTML document — it does not extend base.html, so there is no app banner, nav or Help button, only a back link to the buy list and print CSS (@media print hides the header and the card chrome).
Route
| Route | Behaviour |
|---|---|
GET /po/buylist/{list_id:int}/countlist | Renders the sheet for one saved buy list. Depends(current_user) only — any role may view or print it. |
?autoprint in the querystring triggers window.print() on load (window.onload checks location.search.includes('autoprint')).
Data
ro database:
select cll.sku, cll.bin, cll.requested_at, cll.cleared_at from po_count_list cl join po_count_list_line cll using (count_list_id) where cl.buy_list_id = %s order by coalesce(cll.bin, '~'), cll.sku
Plus _fetch_buy_list(conn, list_id) for the header. Descriptions come from IM2 in a second connection (im2_db() → _items_by_sku()), using description_po and falling back to item_name, then to "".
The '~' sort key parks bin-less lines last (~ sorts after alphanumerics in the default collation).
How rows get created
po_count_list / po_count_list_line are written on buy-list save (po.py ~line 930), not here: for each saved line with cc ticked, one po_count_list header row (buy_list_id, created_by) and one po_count_list_line (count_list_id, sku, bin, buy_line_no). bin is snapshotted at save time as items.primary_bin or items.gen_loc (the gen_loc fallback was added 2026-09-05 — a blank bin printed as "nowhere"). Ticking CC? on a saved line via the inline edit endpoint also sets po_buy_line.line_status = 'Pending Count'; unticking returns it to Ready unless it is Dropped.
The same query (plus count_list_id) is re-run by the buy-list detail page, so the sheet and the on-screen count block always agree.
Known limits / gaps
po_count_list_line.cleared_atis written bypo._clear_counted_count_list_lines(ro_conn, im2_conn, list_id), added 2026-09-06 (Dave: "is there any reason to not fix the po count list?"). It runs on both the buy-list detail page and the printable count list, before either reads the count lines. For every count line withcleared_at is nullit takes the latestcycle_counts.counted_atfor that SKU from the IM2 database and stamps it when that count is newer thanrequested_at. The two tables are in different databases, so the match is done in Python, keyed on(count_list_id, sku)—po_count_list_linehas no surrogate key. Idempotent: the update carriesand cleared_at is null. Verified live against count list 1 with a syntheticcycle_countsrow (cleared 1, then rolled back).- No
count_record/count_postpermission gate on the print route — it is read-only, but any role including level3 can print it. - Counted quantities are not captured anywhere on this sheet; it is paper-only by design.
Shared table sort (2026-09-07)
Column sorting is one implementation in templates/base.html (Dave, 2026-09-07: "all screens that have tables like this need sorts on the appropriate columns"). A table opts in with class="sorttable"; every thead th becomes sortable except those with class="nosort"/class="toggle", an empty heading, or a checkbox in the heading. Client-side only, over the rows already rendered. Details: tech/table_sorting.md.