dow-prod-tracker/src/lib/validators
DJP 7598f4285e Phase 4+5: Dow XLSX ingest API + OMG webhook receiver
Shared canonical path: both ingest channels transform inputs into a single
DowRow shape (src/lib/validators/dow-import.ts) and write via the single
upsertProjectFromDow() function (src/lib/services/dow-excel-service.ts),
so the XLSX importer and the webhook cannot drift. Upserts on
Project.omgJobNumber (unique) — idempotent under replay.

XLSX ingest (Phase 4):
- New src/lib/validators/dow-import.ts — Zod schema with STATUS_MAP,
  RISK_MAP, header normalizer, team-slug normalizer, preview + commit
  result types.
- New src/lib/services/dow-excel-service.ts:
  - parseDowTracker(buffer): locates "Job Tracker " (or "Job Tracker"),
    scans first 5 rows to find the header row with 4+ matched columns,
    skips the example/instructions row at header+1, substring-matches
    headers (handles "Creative Team Member Deliverable is Assigned to"
    → assignee), collects row-level errors without aborting the batch.
  - upsertProjectFromDow(row, organizationId): auto-creates
    ClientTeam if missing (seed covers the 6 canonical teams, but stay
    forgiving); on create, generates N deliverables from outputCount +
    pipeline stages from the default Dow pipeline template with
    BLOCKED/NOT_STARTED status derived from stage dependencies; on
    update, only overwrites fields that are set so producer-edited data
    isn't clobbered by blanks.
  - previewDowImport() and commitDowImport() wrap the flow for the API.
- Rewrote src/app/api/projects/bulk-import/route.ts for the Dow schema.
  POST ?commit=true|false, multipart file=<xlsx>. commit=false returns
  {preview, totalRows, validRows, errors[], rows[]} (first 25 samples);
  commit=true returns {imported, created, updated, deliverablesCreated,
  errors[]}. Batch never aborts on a single bad row.

OMG webhook (Phase 5):
- New src/app/api/webhooks/omg/route.ts — POST-only. HMAC-SHA256
  signature verification via X-OMG-Signature: sha256=<hex> against
  OMG_WEBHOOK_SECRET, timing-safe compare. OMG_WEBHOOK_ALLOW_INSECURE
  escape hatch for stub testing. Looks up the Dow org by canonical
  domain dowjones.com. Transforms the (speculative, documented)
  OMG payload into DowRow then calls upsertProjectFromDow. Unknown
  fields from payload.raw land on Project.customFields JSON so OMG
  can add fields without us losing data. Logs every event (never
  the raw payload — PII).
- middleware.ts: /api/webhooks/ added to the unauthenticated-allowed
  path list (alongside /api/auth and /api/health) — HMAC auth happens
  inside the handler.

Verified: tsc --noEmit ✓ zero errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:57:06 -04:00
..
annotation.ts Add timestamped video annotations with timeline markers (A7.3) 2026-03-18 15:00:23 -05:00
assignment.ts Add artist assignment system and My Work page 2026-02-28 21:27:21 -06:00
comment.ts Add revision tracking and threaded comments for pipeline stages 2026-02-28 21:40:12 -06:00
custom-field.ts feat: add pipeline stage resolver and organization access control 2026-03-14 22:43:43 -05:00
deliverable.ts feat: add validators for PowerPoint and Word documents, and Excel recalculation script 2026-03-02 12:23:09 -06:00
dow-import.ts Phase 4+5: Dow XLSX ingest API + OMG webhook receiver 2026-04-20 18:57:06 -04:00
invitation.ts feat: add pipeline stage resolver and organization access control 2026-03-14 22:43:43 -05:00
notification-rule.ts feat: add pipeline stage resolver and organization access control 2026-03-14 22:43:43 -05:00
permissions.ts feat: add pipeline stage resolver and organization access control 2026-03-14 22:43:43 -05:00
pipeline-template.ts feat: add pipeline stage resolver and organization access control 2026-03-14 22:43:43 -05:00
project.ts Dynamic Pipeline Builder pretty functional now. 2026-03-17 22:09:42 -05:00
revision.ts Add revision tracking and threaded comments for pipeline stages 2026-02-28 21:40:12 -06:00
stage.ts feat: implement stage date override and scheduling features 2026-03-12 23:13:29 -05:00