Skip to content

X-02 · Upload source document (DOCX / PDF)

SOP: SOP_XBRL_Filing.md §6 / Step 2.0 (DRAFT → DOCUMENTS_INGESTED → MAPPING_IN_PROGRESS)Actors: XBRL Preparer (xbrl-preparer@spade.local) — holds INGEST_XBRL_DOCUMENT. Pre-state: Filing at DRAFT. Post-state: Filing at MAPPING_IN_PROGRESS. XbrlSourceDocument + XbrlLineItem rows persisted. XbrlTagMapping rows with confirmedById = NULL + alternatives[].

0. Prerequisites

  • Filing at DRAFT (see X-01).
  • A financial-statement document — DOCX or PDF. Textual tables required; rasterised PDFs will produce zero line items.

TIP

For dev / smoke testing without a real client file, use a DOCX generated from the FS module (see FS-07). For a real fixture, see the xbrl-saas source repo's apps/backend/tests/fixtures/ (port these into packages/xbrl/__tests__/fixtures/).

1. Steps

1.1 Upload via Web UI

  1. Open the filing detail page; switch to Documents tab.
  2. Click Upload source document.
  3. Drag-drop the DOCX or PDF.
  4. The UI shows an upload progress bar; on completion the document appears with parseStatus = PENDING.
  5. Within ~30–60 seconds the status flips through PARSED, line items appear on the Documents tab, then mapping suggestions populate the Mappings tab.

1.2 Upload via API

http
POST /ops/xbrl/filings/<filingId>/documents
Content-Type: multipart/form-data
Authorization: Bearer <xbrl-preparer-jwt>

(form-data: file=<statements.docx>)

The handler:

  1. Asserts INGEST_XBRL_DOCUMENT.
  2. Persists the bytes to S3 at xbrl/<clientId>/<filingId>/source.{docx,pdf} via the File model with fileKind = XBRL_SOURCE_DOCUMENT.
  3. Inserts an XbrlSourceDocument row with parseStatus = PENDING, sha256, mimeType, pageCount (best-effort).
  4. Enqueues ingest-fs-document.
  5. Returns 202 Accepted with the source-document id.

1.3 Worker chain

  1. ingest-fs-document — confirms the file is readable; transitions parseStatus = PENDING → READY; enqueues extract-line-items.
  2. extract-line-items — reads the file:
    • DOCX: uses mammoth to parse tables and headings; identifies statement sections by heading text (Statement of Financial Position, etc.).
    • PDF: uses pdf-parse with table heuristics; falls back to flat-text extraction.
    • Normalises numbers (strip commas; parentheses → negative; currency symbols).
    • Inserts XbrlLineItem rows (statement, label, valueCurrent, valuePrior, sourcePage, sourceLine).
    • Updates parseStatus = PARSED (or FAILED).
    • Transitions filing DRAFT → DOCUMENTS_INGESTED.
    • Enqueues suggest-tag-mappings.
  3. suggest-tag-mappings — for each line item without a mapping:
    • Exact-match against mapping_library.json synonyms (case-insensitive).
    • On miss, fuzzy-match against taxonomy element labels filtered by applicable_entry_points (matches filing.entryPoint) and taxonomy_section (matches line item statement).
    • Inserts XbrlTagMapping with elementId = <top-suggestion>, confidence, alternatives[] (top-N), explanation, confirmedById = NULL.
    • Transitions filing DOCUMENTS_INGESTED → MAPPING_IN_PROGRESS.

2. Verification

Database

sql
SELECT parse_status, page_count FROM xbrl_source_documents WHERE xbrl_filing_id = '<filingId>';
-- expect parse_status = 'PARSED'

SELECT statement, count(*) FROM xbrl_line_items
  WHERE xbrl_filing_id = '<filingId>' GROUP BY statement;

SELECT count(*) AS total,
       count(*) FILTER (WHERE confirmed_by_id IS NULL) AS unconfirmed,
       count(*) FILTER (WHERE confidence = 'HIGH')   AS high,
       count(*) FILTER (WHERE confidence = 'MEDIUM') AS medium,
       count(*) FILTER (WHERE confidence = 'LOW')    AS low
  FROM xbrl_tag_mappings WHERE xbrl_filing_id = '<filingId>';
-- expect total > 0, unconfirmed = total (nothing is human-confirmed yet)
sql
SELECT status FROM xbrl_filings WHERE id = '<filingId>';
-- expect 'MAPPING_IN_PROGRESS'

S3

bash
docker exec breezycorp-monorepo-minio-1 mc ls local/breezycorp/xbrl/<clientId>/<filingId>/
# expect source.{docx,pdf}

Audit log

Event typeNotes
xbrl.document.uploadedAPI hit; payload includes file id + sha256
xbrl.document.parsedextract-line-items succeeded; line-item count payload
xbrl.mappings.suggestedsuggest-tag-mappings produced N rows; payload includes confidence histogram

3. Negative & edge cases

  • Rasterised PDF (no textual layer)extract-line-items yields zero line items; parseStatus = FAILED; filing stays at DRAFT. Ask the client for a DOCX or a text-extractable PDF, or use X-02b · Pull from FS engagement.
  • DOCX without tables — yields a small number of text-only line items at low confidence. Reviewer may discard the upload and request a properly-formatted document.
  • Duplicate upload — sha256 deduplication: the same bytes idempotently return the existing XbrlSourceDocument id; no new line items are inserted.
  • Upload from MAPPING_IN_PROGRESS or later — replacing the source document drops all line items and mappings and resets to DRAFT. Confirms a UI "Are you sure?" prompt; the regression is by design.
  • extract-line-items fails after success of uploadparseStatus = FAILED. Reviewer either retries (transient parser issue) or re-uploads a cleaner source.

Next

Proceed to X-03 · Confirm tag mappings.

Internal use only — BreezyCorp