JUNOHUB-test/src/lib/orders/fulfillmentStatus.ts
talatops 6d45f6ec66 feat(supplier): on-hold reasons, Shopify fulfillment holds, tag + metafield
- Add CachedOrder holdReasonCode/holdNote and ON_HOLD fulfillment status
- Supplier UI: modal for hold reasons; vendor dashboard shows hold details
- Sync to Shopify: native fulfillmentOrderHold (GraphQL) plus JUNO_ON_HOLD tag
  and juno.supplier_hold metafield; release holds when clearing
- Preserve pending supplier fulfillment on webhook cache updates
- Docker volume for gmail-reply-logos; settings note for API scopes

Made-with: Cursor
2026-04-12 17:14:23 +05:00

44 lines
1.3 KiB
TypeScript

export type CanonicalFulfillmentStatus =
| "UNFULFILLED"
| "IN_PROGRESS"
| "ON_HOLD"
| "SHIPPED"
| "FULFILLED"
| "CANCELLED"
const SHOPIFY_FULFILLMENT_MAP: Record<CanonicalFulfillmentStatus, string> = {
UNFULFILLED: "unfulfilled",
IN_PROGRESS: "in_progress",
ON_HOLD: "on_hold",
SHIPPED: "shipped",
FULFILLED: "fulfilled",
CANCELLED: "cancelled",
}
export function normalizeFulfillmentStatus(input: string | null | undefined): CanonicalFulfillmentStatus {
const raw = String(input ?? "")
.trim()
.replace(/[\s-]+/g, "_")
.toUpperCase()
if (raw === "FULFILLED") return "FULFILLED"
if (raw === "SHIPPED") return "SHIPPED"
if (raw === "IN_PROGRESS" || raw === "INPROGRESS" || raw === "PARTIAL" || raw === "PARTIALLY_FULFILLED") {
return "IN_PROGRESS"
}
if (raw === "CANCELLED" || raw === "CANCELED") return "CANCELLED"
if (raw === "ON_HOLD" || raw === "ONHOLD" || raw === "HOLD" || raw === "PENDING_HOLD") {
return "ON_HOLD"
}
return "UNFULFILLED"
}
export function toVendorDisplayStatus(input: string | null | undefined): string {
return SHOPIFY_FULFILLMENT_MAP[normalizeFulfillmentStatus(input)]
}
export function isFulfilledLike(input: string | null | undefined): boolean {
return normalizeFulfillmentStatus(input) === "FULFILLED"
}