Wiki · Workflow companion

Bid award notification

A bid you submitted came back awarded. Parse the award, confirm terms, create the contract record, commit bid_lines → pricing_master, set up customer_programs (PPI, contract pricing), alert fulfillment, and draft the customer acknowledgement.

Mixed · Some steps live, others stubbed
What this is

Bid award notification

A bid award is the moment a months-long bid response becomes real revenue. The cascade has to handle a big asymmetry: NetSuite needs the contract + programs + pricing committed quickly, but the customer-facing acknowledgement should be polished and personalized.

Risk level 4 (high) because the cascade commits real pricing rows into pricing_master that will drive invoices for the contract term. Mistakes here ripple to AR.

The contract is in workflow_definitions WHERE workflow_type='bid_award_notification'. Cascade tables: bids, bid_lines, pricing_master, customer_programs, proposed_actions.

When to use it

Trigger conditions

Heuristic

Precondition bid_open blocks if the bid status isn't in (open, pending_award). This catches the "we already processed this award last week" duplicate path.

Step-by-step what happens

The 4 beats

  1. 01

    Commit pricing from bid_lines → pricing_master

    Each non-deleted bid_lines row INSERTs a corresponding pricing_master row tagged with source='bid_award_{bid_id}'. This is the moment the contract's prices become live.

    Writes pricing_master
    Time ~200ms per 100 lines
    Kind d1_write
    Status stub
  2. 02

    Stage customer_programs (PPI, etc.)

    For each new program defined in the contract (PPI, MDF, contract-specific rebates), a proposed_actions row stages with action_type=create_customer_program. Mike approves the program details before they go live.

    Writes proposed_actions
    Time ~80ms per program
    Kind stage_proposed_action
    Status real
    Note one_per new program in contract
  3. 03

    Notify fulfillment team

    A draft email to the fulfillment team lists the awarded contract, start date, expected weekly volume, and any production heads-up notes. Status=pending_review.

    Writes outbound_email_log
    Time ~80ms
    Kind hitl_email_draft
    Status stub
    Note target · fulfillment_team
  4. 04

    Customer acknowledgement

    A draft email to the awarding customer thanks them, confirms contract terms, and notes the operational handoff. Mike polishes and approves before send.

    Writes outbound_email_log
    Time ~80ms
    Kind hitl_email_draft
    Status stub
    Note target · customer
Outcomes

What's different after the workflow runs

Pricing committed
In D1
≤ 10s
Programs
Staged
one per program
Bid status
awarded
stamped + dated
Emails drafted
2
fulfillment + customer
Failure modes

What can go wrong and how to recover

Bid not in open/pending_award state

Precondition blocks. Likely the award arrived twice; check bids.status and bids.awarded_at. If genuine re-issue, contact the issuer.

Pricing commit partial

If pricing_master INSERT fails midway, the bid stays in pending_award. Retry with POST /admin/bids/<id>/recommit is idempotent — already-committed lines skip via source tag check.

Customer program ambiguity

If the contract terms mention a PPI without clear tier mapping, the proposed_action stages with severity=warn. Mike fills in the tier manually before approving.

No fulfillment heads-up needed

Some bids are renewals — fulfillment already plans for them. Mike can reject the draft fulfillment email; it logs as rejected in outbound_email_log.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='bid_award_notification'
Pricing source tagpricing_master.source='bid_award_<bid_id>'
Award statusbids.status='awarded', bids.awarded_at
Customer program tiercustomer_programs.program_type + value
Reflexion tagbid_award,bid_id:<id>,customer:<id>
Risk level4
Expected duration~60 min
Triggerevent · sources=inbound_email, portal_webhook, manual
// Pricing commit is idempotent via source tag await db.run( "INSERT OR IGNORE INTO pricing_master (customer_id, item_code, case_price, source, school_year) " + "SELECT customer_id, item_code, proposed_price, 'bid_award_' || ?1, school_year " + "FROM bid_lines WHERE bid_id=?1 AND deleted_at IS NULL", [bid_id] );