RO Import History
RO Import History — Technical Spec
Router: app/ro.py (ro_router, mounted at /ro after ro_import.router). Template: app/templates/ro_imports.html. Database: the ro database (ro_db()), not IM2.
Routes
| Method | Path | Notes |
|---|---|---|
| GET | /ro/imports | List screen. Query params search, mode, source_system, operator, days. |
| GET | /ro/imports/export | CSV of the same filter, limit=100000. Filename RO_Import_History_<UTC date>.csv. |
Both depend on current_user only — any signed-in role can read the log, including level3. There is no write route: the log is append-only, written by application code, never by the operator.
The same query also powers the imports block on the order detail screen (query_imports(conn, order_no=...), ro.py order route).
Data
ro_import_log, one row per import attempt, joined left join ro_header h on h.order_no = l.order_no for job_name / ro_type.
Columns written by log_import(): order_no, mode, operator, source_system, source_ref, source_kind, file_name, file_id, rev_no, lines_loaded, lines_changed, issues_raised, outcome, note (+ import_id, imported_at defaults).
source_system on this table carries the same CHECK constraint as ro_inbox / ro_header (see app/ro_import.py header comment — 'MANUAL' was added 2026-09-04 for uploads and hand-keyed orders).
Filtering (query_imports)
search: if all digits →order_no = %s OR source_ref = %s OR file_name ILIKE %%s%%; otherwisefile_name / source_ref / h.job_name ILIKE.mode,source_system,operator: exact match.days:imported_at >= now() - (%s || ' days')::interval, digits only.- Order
imported_at desc, import_id desc,limit LIST_LIMITon the screen. - Dropdown option sources:
IMPORT_MODES = ["Auto","Manual"],IMPORT_SOURCES = ["FP","BT","Upload"](ro.py:594-595), operatorsselect distinct operator from ro_import_log(import_operators()).
Rendering rules
outcome == 'Loaded'renders the greenpill-ok; every other value renders red (pill-bad) — there is no third state in the template.noterenders under the outcome when present; it is not a separate column on screen but is a column in the CSV (IMPORT_COLUMNS).source_kindis not a column; it is thetitle=tooltip on File used.rev_no,lines_loaded,lines_changed,issues_raisedrender blank when NULL.imported_at.strftime('%Y-%m-%d %H:%M')— server timezone, no conversion.- Row count line switches to "Showing first N of M" when
total > limit; there is no pagination on this screen, only the filter.
Known limits / defects
log_import()has no callers anywhere in the package (verified by grep overapp/.pyandscripts/on 2026-09-06). The reader, the export and the order-detail block are all built; nothing in the shipped code writes the row. Any rows inro_import_logtoday came from outside this code. Until an import path callslog_import(), this screen under-reports.- Source filter values do not match what the importer stores. The dropdown offers
FP / BT / Upload, whilero_import.pyusesFP / BT / XLSXand, per Dave's 2026-09-04 ruling, stages uploads and hand-keyed orders asMANUAL. Selecting Upload will match nothing. - No pagination — a filter wide enough to exceed
LIST_LIMITsilently truncates the screen (the export is not truncated). - The export writes raw values, including
imported_atas a Python timestamp repr, not a formatted date string.
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.