Skip to content

Order Processing Workflow

Complete workflow from document receipt to ERP submission, showing all intake paths, the shared validation pipeline, and every feature-flag decision point.

flowchart TD
%% ── Styles ──────────────────────────────────────────────────────────────
classDef active fill:#d4edda,stroke:#28a745,color:#000
classDef inactive fill:#fff,stroke:#dc3545,color:#999,stroke-dasharray:5 5
classDef flag fill:#fff3cd,stroke:#ffc107,color:#000
classDef process fill:#e2e3e5,stroke:#6c757d,color:#000
classDef convergence fill:#cce5ff,stroke:#007bff,color:#000
%% ── Email Intake Paths ──────────────────────────────────────────────────
subgraph EMAIL["Email Intake"]
E2["Cloudflare Email Worker\n(POST /api/webhook/inbound-email)"]:::active
E3["Gmail Poll\n(ScheduledEmailPullDispatcher)"]:::active
end
%% Cloudflare path
E2 -->|"HMAC verify → insert emailEvent\n→ link pdfDocuments"| CF_EXTRACT
CF_EXTRACT["Start PDFExtractionWorkflow\nper attachment"]:::active
%% Per-connection poll path
E3 -->|"poll_gmail_for_connection β†’\nfan-out per message"| EPW
%% ── Email Processing Workflow ───────────────────────────────────────────
EPW["EmailProcessingWorkflow\n(Temporal)"]:::active
EPW -->|"1. fetch_gmail_message\n2. lookup_alias\n3. triage_email"| TRIAGE
TRIAGE{"Triage\nDecision"}:::process
TRIAGE -->|"skip / rfq"| DONE_EMAIL["Email Complete\n(skipped or saved as RFQ)"]:::process
TRIAGE -->|"order"| EMAIL_EXTRACT["4. extract_from_email\n5. match_customer\n6. batch_match_items\n7. save_email_order"]:::active
EMAIL_EXTRACT --> AV_CHILD
%% ── PDF Upload Path ─────────────────────────────────────────────────────
subgraph UPLOAD["PDF / Document Upload"]
U1["Manual Upload\n(POST /api/pdf-documents)"]:::active
end
U1 -->|"Upload to R2 β†’ insert pdfDocument\n+ workItem β†’ startDocumentProcessing"| PDFWF
CF_EXTRACT --> PDFWF
%% ── PDF Extraction Workflow ─────────────────────────────────────────────
PDFWF["PDFExtractionWorkflow\n(Temporal)"]:::active
PDFWF -->|"0. pre_extract_document\n1. extract_text_with_gemini_multiformat\n2. match_customer\n3. batch_match_items\n3.5. enrich_order_with_inventory_routing\n3.6. resolve_org_discounts\n4. save_extracted_order"| AV_CHILD
%% ── Convergence: Agentic Validation ─────────────────────────────────────
AV_CHILD["Start child:\nAgenticValidationWorkflow"]:::convergence
AV_CHILD --> FLAG_AV
FLAG_AV{{"agentic_validation_enabled\n(default: false)"}}:::flag
FLAG_AV -->|"false"| AV_DISABLED["Persist 'disabled' status\n→ advance work_item\nto manual review"]:::active
FLAG_AV -->|"true"| AV_RUN
AV_RUN["1. Fetch ERP capabilities\n2. LLM Validation (Gemini)\n3. persistAutoPricedMatches\n4. ERP item validity check\n5. Merge results & compute confidence"]:::active
AV_RUN --> FLAG_THRESHOLD
FLAG_THRESHOLD{{"agentic_confidence_threshold\n(default: 0.95)"}}:::flag
FLAG_THRESHOLD -->|"confidence β‰₯ threshold\n& all_valid"| AUTO_SUBMIT_DECISION
FLAG_THRESHOLD -->|"confidence < threshold\nor issues found"| ESCALATED["Escalated\n→ Manual Review Queue"]:::active
AUTO_SUBMIT_DECISION{{"agentic_auto_submit_enabled\n(default: false)\nAND\nerp_write_enabled\n(default: false)"}}:::flag
AUTO_SUBMIT_DECISION -.->|"both true\n(INACTIVE)"| AUTO_SUBMIT["submit_order_to_erp\n→ auto_submitted"]:::inactive
AUTO_SUBMIT_DECISION -->|"either false\n(current default)"| DRY_RUN["Dry Run\n→ log decision only"]:::active
%% ── Manual Submit Path (Active) ─────────────────────────────────────────
ESCALATED --> MANUAL_REVIEW
AV_DISABLED --> MANUAL_REVIEW
DRY_RUN --> MANUAL_REVIEW
MANUAL_REVIEW["Operator Reviews Order\nin Webapp UI"]:::active
MANUAL_REVIEW -->|"Generate Quote\n(POST /api/erp/orders/generate-quote)"| TEMPORAL_QUOTE
MANUAL_REVIEW -->|"Submit Order\n(POST /api/erp/orders/[id]/submit)"| SUBMIT_ORDER
TEMPORAL_QUOTE["ERPQuoteGenerationWorkflow\n(Temporal, async 202)"]:::active
SUBMIT_ORDER["Manual Submit\n(POST /api/erp/orders/[id]/submit)"]:::active
SUBMIT_ORDER --> TEMPORAL_SUBMIT["ERPOrderSubmissionWorkflow\n(Temporal, async 202)"]:::active
TEMPORAL_QUOTE --> ERP_DONE
TEMPORAL_SUBMIT --> ERP_DONE
ERP_DONE["Order in ERP βœ“"]:::active
StyleMeaning
Green solid borderActive path β€” currently in use with default flag values
Red dashed borderInactive path β€” requires feature flags to be enabled
Yellow diamond/hexagonFeature flag decision point
Blue boxConvergence point where multiple paths merge
Grey boxIntermediate processing step or terminal state
PathTriggerEntry PointTemporal Workflow
Cloudflare Email WorkerCloudflare Worker parses inbound email, uploads attachments to R2, posts webhookPOST /api/webhook/inbound-emailPDFExtractionWorkflow (per attachment)
Gmail PollTemporal schedule (per-connection mailbox poll every N minutes)ScheduledEmailPullDispatcherEmailProcessingWorkflow (per message)
PDF UploadUser uploads PDF/Excel via webapp UIPOST /api/pdf-documentsPDFExtractionWorkflow

All paths converge on AgenticValidationWorkflow, which runs as a child workflow of either EmailProcessingWorkflow or PDFExtractionWorkflow.

FlagTypeDefaultEffect
agentic_validation_enabledbooleanfalseGates the entire AgenticValidationWorkflow. When off, orders skip validation and go directly to the manual review queue with a disabled status.
agentic_auto_submit_enabledbooleanfalseAllows automatic ERP submission after successful agentic validation. Requires agentic_validation_enabled. When off, passing validation results in a dry_run (decision logged but not submitted).
agentic_confidence_thresholdmultivariate0.95Minimum confidence score for auto-submit. Orders below this threshold are escalated to manual review regardless of other flags.
erp_write_enabledbooleanfalseGlobal safety switch allowing the agentic workflow to write to ERP systems. Must be true alongside agentic_auto_submit_enabled for actual submission. When off, auto-submit decisions become dry runs.
email_enabledbooleanfalseMaster switch for email functionality (inbox, aliases, email-to-order).

Retired (2026-06): use_temporal_for_erp_writes, use_temporal_for_quote_writes, and use_temporal_for_order_writes are no longer used. All manual ERP write paths (quote generation, order creation, order sync) now route through Temporal unconditionally; the synchronous P21 fallback and these flags have been removed.

  1. Email/PDF intake paths are always active (email processing requires email_enabled per-org).
  2. Agentic validation is disabled β€” orders go straight to the manual review queue.
  3. Manual submit uses a Temporal workflow (ERPOrderSubmissionWorkflow) for the POST /api/erp/orders/[id]/submit path.
  4. Quote generation uses a Temporal workflow (ERPQuoteGenerationWorkflow) for the POST /api/erp/orders/generate-quote path β€” unconditional, same as order creation and sync.
  5. Auto-submit is completely inactive β€” even if validation were enabled and passed, agentic_auto_submit_enabled and erp_write_enabled both default to off.