Skip to content

Cleveland Kitchen Org-Specific Discounts — Full-Stack Implementation Plan

Date: 2026-04-08
Status: Draft for team review
Scope: Extraction → persistence → review state → WhereFour submission
Customer: Cleveland Kitchen
Design goal: org-specific customization implemented in a reusable way behind feature flags


Cleveland Kitchen’s new data package changes discount handling from a UI-only pricing warning into a structured end-to-end feature.

The two source files are:

  • Finished Goods - UPC Mapping.xlsx
  • CustomLineItems.csv

These confirm that:

  1. UPC is not the canonical key

    • Article Code is the internal product identity.
    • RefCode is the UPC-oriented alias layer.
    • The same product family can exist as different sellable SKUs by unit of measure (each, case, etc.).
    • Pack size often lives in the description, so UPC-only matching is unsafe.
  2. Discounts are not net-price edits

    • Discounts must be emitted as separate WhereFour custom line items.
    • Those custom line items already exist in CK’s system.
    • They preserve downstream accounting / GL treatment into QuickBooks.
    • Discounts are applied at the SKU line level, not as a single order-level adjustment.

The implementation should therefore create a reusable pipeline with these characteristics:

  • org-specific extraction behavior
  • reference-data-backed canonical identity resolution
  • explicit discount planning and validation
  • persisted review-state metadata
  • feature-flagged activation per org

Based on the working session and follow-up:

  • Ordermatic will resolve the correct SKU from messy inbound PO identifiers.
  • Ordermatic will treat discounts as separate allowance/custom line items, not by rewriting product prices.
  • The system will validate claimed discounts against the active promotion context.
  • Reviewers will clearly understand why the ERP price differs from the customer PO.
  • Accounting-safe treatment must be preserved downstream.

That means the implementation must preserve gross line pricing and emit explicit discount lines.


Org-specific extraction inputs already exist

Section titled “Org-specific extraction inputs already exist”
  • packages/db/src/schema/erp-connections.ts
    • promptConfig
    • extraConfig
  • packages/pdf-shared/pdf_shared/crud.py
    • get_document_and_connection_config() already loads:
      • prompt_config
      • extra_config.extraction_schema

Prompt composition already supports org-specific instructions

Section titled “Prompt composition already supports org-specific instructions”
  • packages/pdf-shared/pdf_shared/utils/langfuse_prompts.py
    • get_pdf_extraction_prompt_from_config(...)
    • build_extraction_schema_instructions(...)
  • packages/pdf-shared/pdf_shared/llm/dspy_org_module.py
    • org overlay prompt support already exists conceptually

Review state and extraction schema system already exist

Section titled “Review state and extraction schema system already exist”
  • apps/webapp/src/types/order.ts
  • apps/webapp/src/lib/extraction-schema.ts
  • apps/webapp/src/stores/pdfOrder/order.ts
  • apps/webapp/src/components/orders/DocumentOrderReview.tsx
  • apps/webapp/src/components/orders/OrderEditor.tsx

No structured discount model exists end-to-end

Section titled “No structured discount model exists end-to-end”
  • ExtractedOrderData / ExtractedOrderItemData do not contain discount-specific fields.
  • packages/db/src/schema/pdf-documents.ts does not store discount review metadata.
  • apps/temporal-worker/activities/database.py::save_extracted_order() persists only a narrow set of fields.
  • apps/webapp/src/components/orders/DocumentOrderReview.tsx
    • uses isCKPOCEnabled
    • injects isCKPOCNewCustomerSkuPairing: true as a synthetic demo flag
  • This is not driven by real extracted metadata.
  • apps/webapp/src/stores/pdfOrder/order.ts
    • $orderTotals only sums quantity * unitPrice
  • apps/webapp/src/components/orders/OrderTotalsCard.tsx
    • only displays subtotal/tax/total

Existing WhereFour validation route is sidecar/PoC only

Section titled “Existing WhereFour validation route is sidecar/PoC only”
  • apps/webapp/src/pages/api/integrations/wherefour/validate-order.ts
  • apps/webapp/src/pages/orders/wherefour-submit.astro

This route is useful as a proving ground, but the real implementation should be wired into the normal PDF review experience.


  1. Canonical identity first, heuristics second
  2. Postgres is source of truth; Redis is derived cache
  3. Gross price stays on product line
  4. Discount becomes a separate custom line item
  5. Review page displays persisted backend decisions, not UI-only calculations
  6. Org-specific behavior is configurable, not hardcoded
  7. Feature flags control activation and rollout

Customer PO / Email / PDF / XLS(X)
-> Extraction prompt with org-specific instructions
-> Structured extracted fields (UOM, pack size, claimed discounts, UPC aliases)
-> Customer + item matching
-> Org discount resolver
- canonical SKU/UOM resolution
- promo validation
- proposed custom line item generation
-> Persist review metadata
-> Review page renders discount-aware state
-> On submit, WhereFour uses persisted proposed custom lines

Continue using erp_connections.extraConfig and add a structured discountConfig block.

Example:

{
"discountConfig": {
"enabled": true,
"mode": "separate_custom_line_items",
"pricingDisplayMode": "gross_plus_allowance",
"requiresPromotionValidation": true,
"identityResolver": "ck_wherefour_v1",
"referenceDataVersion": "2026-04-07"
}
}

Add new tables in packages/db keyed by erpConnectionId.

Purpose: canonical product identity + alias lookup

Fields:

  • id
  • erpConnectionId
  • canonicalProductId
  • articleCode
  • refCode
  • aliasValue
  • aliasType (article_code, ref_code, upc12, upc14, description_alias)
  • unitOfMeasure
  • packSize
  • isActive
  • versionTag
  • timestamps

Purpose: imported custom line item catalog from CK

Fields:

  • id
  • erpConnectionId
  • externalId (barcode / OID / WhereFour identifier)
  • name
  • publicDescription
  • lineItemCalculationType
  • unitOfMeasurement
  • active
  • amount
  • percentage
  • startMonth
  • startDay
  • endMonth
  • endDay
  • glMetadata jsonb
  • timestamps

Purpose: explicit rule layer mapping customer/SKU/UOM/location to custom line item behavior

Fields:

  • id
  • erpConnectionId
  • customerKey
  • locationKey
  • canonicalProductId
  • unitOfMeasure
  • customLineItemId
  • ruleType
  • effectiveStart
  • effectiveEnd
  • metadata jsonb
  • timestamps
  • orgExtractionMetadata jsonb
  • discountSummary jsonb (optional if not folded into orgExtractionMetadata)
  • unitOfMeasure text
  • orgExtractionMetadata jsonb

This avoids forcing a CK-only schema into top-level columns while still making the review state queryable and durable.


  • Finished Goods - UPC Mapping.xlsx
  • CustomLineItems.csv
  • parse the workbook
  • normalize alias values
  • preserve raw values and normalized values
  • generate canonical identity per Article Code + UOM
  • store:
    • article code
    • ref code
    • UPC12
    • UPC14
    • description alias
    • pack-size metadata
  • parse custom line item catalog
  • preserve:
    • active/inactive status
    • additive/subtractive behavior
    • amount/percentage
    • effective windows
    • downstream GL/accounting metadata if available
  • first pass can be script-driven / one-off ingestion
  • medium-term should become a repeatable admin import path or Dagster sync job
  • Redis indexes should be materialized from Postgres after import

Teach extraction to identify discount-related information without netting prices directly into product lines.

  • packages/pdf-shared/pdf_shared/llm/models.py
  • packages/pdf-shared/pdf_shared/llm/schemas.py

Add optional structured item fields:

  • unit_of_measure
  • pack_size_text
  • raw_upc
  • article_code
  • ref_code
  • claimed_discount_text
  • claimed_discount_amount
  • claimed_discount_percent
  • discount_scope (line_item, order, unknown)

Add optional order-level fields:

  • location_or_warehouse
  • claimed_discount_summary_text

Prompt behavior for org-specific discounts

Section titled “Prompt behavior for org-specific discounts”

Extend org prompt instructions so the model is told:

  • extract gross product pricing
  • detect discounts/allowances separately
  • extract UOM and pack size explicitly
  • do not fold net discounts into product line pricing
  • capture identifiers (Article Code, RefCode, UPC) whenever present
  • erp_connections.promptConfig.requirements
  • erp_connections.promptConfig.notes
  • extraConfig.extraction_schema
  • optional Langfuse org overlay

Create a shared resolver module that runs after extraction and item matching.

Responsibilities:

  1. Canonical item resolution

    • use article code, ref code, UPC12/14, description alias, pack size, and UOM
    • detect ambiguity rather than guessing
  2. Discount planning

    • inspect claimed discount metadata from extraction
    • validate against active rule window
    • match correct custom line item
    • produce proposedCustomLineItems
  3. Review metadata assembly

    • attach provenance
    • attach notes
    • attach confidence / ambiguity status

At item level:

{
"identityResolution": {
"articleCode": "1616MK",
"matchedBy": "ref_code",
"confidence": 0.98,
"unitOfMeasure": "case",
"packSize": "6/16 oz",
"ambiguous": false
},
"discount": {
"claimed": {
"amount": 1.5,
"scope": "line_item",
"text": "promotional allowance"
},
"proposedCustomLineItem": {
"id": "OID-031",
"name": "1616BR UNFI Promotional Allowance"
},
"status": "validated",
"promoWindow": {
"start": "2026-01-01",
"end": "2026-12-31"
}
}
}

At order level:

{
"discountReview": {
"enabled": true,
"status": "validated",
"grossSubtotal": 86.0,
"discountTotal": 6.0,
"netSubtotal": 80.0,
"notes": ["Promo validated", "Allowance line will be created"]
}
}

Update:

  • apps/temporal-worker/workflows/pdf_extraction.py
  • apps/temporal-worker/activities/database.py
  • likely new activity module for org discount resolution

Insert a new step between item matching and persistence:

  1. extract
  2. match customer
  3. match items
  4. resolve org discounts
  5. save extracted order + item metadata
  6. agentic validation / next workflow steps

This ensures the review page is driven by persisted backend decisions, not recomputed UI logic.


Update response so completed extracted orders include:

  • order-level discount summary metadata
  • item-level org extraction metadata
  • unitOfMeasure

File:

  • apps/webapp/src/pages/api/pdf-documents/[id].ts

Same expanded payload for direct order fetch.

File:

  • apps/webapp/src/pages/api/extracted-orders/[id]/index.ts

Support reviewer-driven persistence of:

  • validated discount state
  • reviewer notes on discount ambiguity
  • manual overrides to metadata where allowed

This does not mean allowing reviewers to net-edit price. It means allowing them to validate or reject discount metadata.


File:

  • apps/webapp/src/types/order.ts

Add to ExtractedOrderData:

  • discountSummary?
  • orgExtractionMetadata?

Add to ExtractedOrderItemData:

  • unitOfMeasure?
  • orgExtractionMetadata?

File:

  • apps/webapp/src/stores/pdfOrder/order.ts

Make $orderTotals compute:

  • grossSubtotal
  • discountTotal
  • netTotal

Do not derive discount purely from quantity * unitPrice; use persisted metadata.

Files:

  • apps/webapp/src/components/orders/DocumentOrderReview.tsx
  • apps/webapp/src/components/orders/OrderItemCard.tsx
  • apps/webapp/src/components/orders/OrderTotalsCard.tsx
  • apps/webapp/src/components/orders/OrderEditor.tsx
  • apps/webapp/src/components/integrations/WhereFourDiscountChip.tsx

Current state:

  • fake isCKPOCNewCustomerSkuPairing block

New state:

  • render from real item/order metadata
  • show:
    • discount summary
    • per-line allowance preview
    • ambiguity / invalid promo states
    • lock/warning against manual price mutation when discount is validated

If a line has a validated discount plan:

  • product unit price remains gross
  • allowance/custom line preview is shown beneath it
  • reviewer should not silently overwrite price to net value

The submission path should use the persisted proposedCustomLineItems from review metadata.

That means:

  • product lines stay gross
  • discount lines become custom_line_items
  • no duplicate pricing logic at submit time
  • no frontend-derived pricing math at submit time
  • apps/temporal-worker/workflows/wherefour_order_submission.py
  • apps/temporal-worker/activities/wherefour.py
  • keep actual discount submission behind a separate feature flag until sandbox-tested

  • use_org_discounts
    • enables extraction + persistence + review rendering
  • use_org_discount_submission
    • enables actual custom-line submission to WhereFour
  • wherefour-order-validation
    • keep existing POC route alive until main review integration supersedes it
  1. CK only: use_org_discounts = true, use_org_discount_submission = false
  2. Validate extraction + review state on historical docs
  3. Enable sandbox submission
  4. Validate emitted custom lines
  5. Enable production submission for CK
  6. Generalize for future orgs via config + reference data import

  • add DB tables for product aliases / custom line items / discount rules
  • add orgExtractionMetadata fields
  • extend erp_connections.extraConfig typing with discountConfig
  • build importer for UPC workbook
  • build importer for custom line item catalog
  • materialize lookup caches from Postgres
  • extend extraction models/schemas with structured discount/UOM/pack fields
  • update prompt composition to include org discount instructions
  • add canonical SKU/UOM resolution
  • add discount validation + custom line planning
  • persist results into extracted order/item metadata
  • expand API responses
  • update types/order.ts
  • update store totals
  • replace fake CK review banner with real metadata-driven UI
  • WhereFour submission consumes proposedCustomLineItems
  • discount lines emitted as actual custom line items
  • product unit price remains gross
  • regression fixtures
  • ambiguity tests
  • feature-flag rollout docs
  • CK UAT sign-off

  • leading-zero / trailing-digit UPC variants
  • shared UPC root across different pack sizes
  • same product with each and case
  • valid promo window
  • expired promo window
  • missing custom line item mapping
  • multi-line discount orders
  • 0 silent SKU mismatches caused by normalization
  • 0 net-price mutation when custom allowance line is required
  • 100% of emitted discount lines map to approved catalog entries
  • ambiguous promo or SKU cases escalate to review

Risk: wrong SKU match
Mitigation: canonical model + ambiguity escalation

Risk: wrong each vs case choice
Mitigation: persist pack-size evidence and confidence

Risk: accounting corruption
Mitigation: UI lock/warning and separate allowance preview

4. Submission logic diverges from review logic

Section titled “4. Submission logic diverges from review logic”

Risk: reviewer sees one thing, ERP gets another
Mitigation: submit from persisted proposed custom line items only


If we want the smallest safe production-shaping slice first:

  1. add schema/storage for org extraction metadata
  2. import CK product + custom line reference data
  3. extend extraction result contract with discount/UOM/pack fields
  4. persist review metadata
  5. render on review page
  6. leave actual WhereFour discount submission disabled

That gives us a reviewable, auditable implementation before we risk writing anything into CK’s ERP flow.


Do not implement this as a chip plus heuristics.

Implement it as:

  • org-specific prompt/config
  • canonical reference data in Postgres
  • shared discount resolver
  • persisted review metadata
  • feature-flagged review rendering
  • submission reusing the same metadata

That is the cleanest way to support Cleveland Kitchen now while keeping the feature reusable for future orgs.