Run persistence, per-entry action layer with versioning and rollback, ITC re-scoring on corrections, reminder system, and vendor intimation CSV export, the CA's working surface on top of the reconciliation engine.
The engine produces ephemeral results. The workbench adds persistence, structured decision-making, and audit trail on top.
reconciliation_runs header with summary counts + ITC totals. Bulk-inserts all entries into reconciliation_results with entry snapshots, risk scores, and source document links.match_category.entry_actions row. Updates user_action_status. For corrections: calls AI /risk to re-score ITC. For reminders: inserts into reminders table.intimate_vendor. Dismiss reminders from sidebar panel.Every reconciliation result can be saved as a named run for comparison, audit, and structured follow-up.
POST /api/sessions/:id/runs, persists the current engine output with document links and risk scores.
books_document_id and gstr2b_document_id for "View Source File"risk_score, risk_priority, risk_sla_days, risk_factors[]parsed_invoices rowsMap<string, {id, document_id}>GSTIN|invoice_number_norm composite keySaved runs appear in an accordion. Load, rename, delete, or compare across runs.
GET /api/sessions/:id/runs, list with summary countsGET /api/sessions/:id/runs/:runId, full entries + current actionsPATCH, rename run label (inline edit)DELETE, cascade deletes results, actions, and remindersuser_action_statusEach entry row expands to show a side-by-side comparison and category-specific action buttons.
Inline expansion below each entry row. Two-column layout with Books (left) and GSTR-2B (right) records.
POST /action with enter_correction/risk endpoint with corrected entry valueswarnings[] in responseEvery action creates a new version. Previous versions are preserved for audit and rollback.
db.begin() for atomicityis_current=false, superseded_at=NOW() on all current actionsversion=prev+1, is_current=trueis_current=true per result_iddb.begin()is_current=trueuser_action_status on the result rowWhen a CA enters a corrected value that resolves a mismatch, the system re-scores the entry's ITC risk in real time.
corrected_books_value / corrected_gstr2b_value.raw_entry_snapshot. If corrected values match within ₹1, sets match_category: MATCHED and clears mismatch_types.POST http://AI_SERVICE/risk. Existing ITC risk scorer evaluates with new values.risk_score, risk_priority, risk_sla_days, risk_factors[] written to the result row. UI updates immediately.warnings[] array: "Risk re-scoring failed, scores may be stale".CAs can set follow-up reminders on specific entries. Reminders surface via a sidebar badge and a dedicated panel.
app_settings.reminders (frequency + time)reminders table with links to session, run, and resultmissing_in_books_verify, missing_in_gstr2b_followup, mismatch_reviewGET /api/reminders/count every 60 secondsPATCH /api/reminders/:idPUT /api/settings/reminders in app_settings tablecancelled flag prevents state updates after unmountBulk export a CSV of entries flagged for vendor follow-up.
?filter=intimate_vendor selects entries with user_action_status = 'intimate_vendor'. Default: all MISSING_IN_GSTR2B entries.Content-Type: text/csv with Content-Disposition: attachment. Values with commas/quotes are properly escaped using RFC 4180 double-quote rules.Three new migrations (009 to 011) add five tables/extensions to the existing schema.
id: UUID PK session_id: UUID FK → reconciliation_sessions label: TEXT # "Run 1", user-renameable run_at: TIMESTAMPTZ total_books: INT total_gstr2b: INT matched: INT mismatched: INT missing_in_gstr2b: INT missing_in_books: INT probable_matches: INT auto_resolved: INT total_itc_claimable: NUMERIC(15,2) total_itc_at_risk: NUMERIC(15,2) total_itc_blocked: NUMERIC(15,2) # INDEX: session_id
# Columns added to existing table by migration 009: run_id: UUID FK → reconciliation_runs entry_key: TEXT # "GSTIN|inv_num_norm" raw_entry_snapshot: JSONB # full engine entry dict books_document_id: UUID FK → documents gstr2b_document_id: UUID FK → documents user_action_status: TEXT DEFAULT 'pending' corrected_books_value: NUMERIC(15,2) corrected_gstr2b_value: NUMERIC(15,2) # INDEX: run_id, (run_id, user_action_status), entry_key
id: UUID PK result_id: UUID FK → reconciliation_results run_id: UUID FK → reconciliation_runs entry_key: TEXT NOT NULL action_type: TEXT NOT NULL corrected_value: NUMERIC(15,2) correction_field: TEXT # 'books_value' or 'gstr2b_value' notes: TEXT # Versioning: version: INT DEFAULT 1 is_current: BOOLEAN DEFAULT true superseded_at: TIMESTAMPTZ acted_at: TIMESTAMPTZ DEFAULT NOW() # UNIQUE: (result_id) WHERE is_current = true
id: UUID PK session_id: UUID FK → reconciliation_sessions run_id: UUID FK → reconciliation_runs result_id: UUID FK → reconciliation_results entry_key: TEXT NOT NULL supplier_gstin: TEXT invoice_number: TEXT reminder_type: TEXT NOT NULL status: TEXT DEFAULT 'pending' # pending|dismissed|snoozed notes: TEXT due_at: TIMESTAMPTZ dismissed_at: TIMESTAMPTZ # INDEX: (session_id, status), (status, due_at)
key: TEXT PK value: JSONB NOT NULL updated_at: TIMESTAMPTZ DEFAULT NOW() # Seeded with: # key='reminders', value='{"frequency":"daily","time":"10:00"}' # UPSERT via ON CONFLICT (key) DO UPDATE
15 new endpoints added to server.ts.
| Method | Route | Purpose |
|---|---|---|
| POST | /api/sessions/:id/runs | Save current reconciliation as a named run |
| GET | /api/sessions/:id/runs | List saved runs with summary counts |
| GET | /api/sessions/:id/runs/:runId | Load full run: entries + current actions |
| PATCH | /api/sessions/:id/runs/:runId | Rename run label |
| DELETE | /api/sessions/:id/runs/:runId | Delete run (cascades to results, actions, reminders) |
| POST | /api/runs/:runId/entries/:resultId/action | Create versioned action on an entry |
| GET | /api/runs/:runId/entries/:resultId/history | Fetch all action versions for an entry |
| POST | /api/runs/:runId/entries/:resultId/rollback | Rollback to previous action version |
| GET | /api/sessions/:id/runs/:runId/export | Download vendor intimation CSV |
| GET | /api/settings | Get all app settings |
| PUT | /api/settings/:key | Upsert a setting value |
| GET | /api/reminders | List pending reminders (filterable by session_id) |
| GET | /api/reminders/count | Pending reminder count (for sidebar badge) |
| PATCH | /api/reminders/:id | Dismiss or snooze a reminder |
All files involved in the workbench feature, grouped by layer.