import { assertNoUserErrors, clampInt, moneyFromSet, nodesFromConnection, pageInfoFromConnection, shopifyGraphql, trimString, } from './lib/client.ts' import { MONEY_SET_FIELDS, PAGE_INFO_FIELDS } from './lib/fragments.ts' import { toGid } from './lib/shop.ts' import type { Money, ShopifyClientOptions, ShopifyPageInfo } from './types.ts' export type DraftOrderSummary = { id: string name: string | null status: string | null email: string | null createdAt: string | null updatedAt: string | null totalPrice: Money | null invoiceUrl: string | null } function mapDraft(node: Record): DraftOrderSummary { return { id: String(node.id || ''), name: typeof node.name === 'string' ? node.name : null, status: typeof node.status === 'string' ? node.status : null, email: typeof node.email === 'string' ? node.email : null, createdAt: typeof node.createdAt === 'string' ? node.createdAt : null, updatedAt: typeof node.updatedAt === 'string' ? node.updatedAt : null, totalPrice: moneyFromSet(node.totalPriceSet), invoiceUrl: typeof node.invoiceUrl === 'string' ? node.invoiceUrl : null, } } /** * List draft orders. * * @example * import { listDraftOrders } from 'kody:@kody/shopify/draft-orders' * const { items } = await listDraftOrders({ shop: 'acme', query: 'status:open' }) */ export async function listDraftOrders( params: ShopifyClientOptions & { query?: string first?: number after?: string } = {}, ) { const first = clampInt(params.first, 1, 50, 20) const data = await shopifyGraphql<{ draftOrders: unknown }>({ shop: params.shop, apiVersion: params.apiVersion, query: `query ListDraftOrders($first: Int!, $after: String, $query: String) { draftOrders(first: $first, after: $after, query: $query) { ${PAGE_INFO_FIELDS} nodes { id name status email createdAt updatedAt invoiceUrl totalPriceSet { ${MONEY_SET_FIELDS} } } } }`, variables: { first, after: trimString(params.after) || null, query: trimString(params.query) || null, }, }) return { items: nodesFromConnection>(data.draftOrders).map( mapDraft, ), pageInfo: pageInfoFromConnection(data.draftOrders) as ShopifyPageInfo, } } /** * Get one draft order by GID or numeric id. */ export async function getDraftOrder( params: ShopifyClientOptions & { id: string }, ): Promise { const id = toGid('DraftOrder', params.id) const data = await shopifyGraphql<{ draftOrder: Record | null }>({ shop: params.shop, apiVersion: params.apiVersion, query: `query GetDraftOrder($id: ID!) { draftOrder(id: $id) { id name status email createdAt updatedAt invoiceUrl totalPriceSet { ${MONEY_SET_FIELDS} } } }`, variables: { id }, }) if (!data.draftOrder) throw new Error(`Draft order not found: ${id}`) return mapDraft(data.draftOrder) } export type CreateDraftOrderParams = ShopifyClientOptions & { email?: string note?: string lineItems: Array<{ title?: string originalUnitPrice?: string quantity: number variantId?: string }> dryRun?: boolean } /** * Create a draft order. Defaults to dry-run; pass `dryRun: false` to write. */ export async function createDraftOrder(params: CreateDraftOrderParams) { if (!params.lineItems?.length) { throw new Error('createDraftOrder requires at least one line item.') } const input = { email: trimString(params.email) || undefined, note: trimString(params.note) || undefined, lineItems: params.lineItems.map((item) => ({ title: trimString(item.title) || undefined, originalUnitPrice: trimString(item.originalUnitPrice) || undefined, quantity: item.quantity, variantId: trimString(item.variantId) ? toGid('ProductVariant', item.variantId) : undefined, })), } if (params.dryRun !== false) { return { dryRun: true as const, wouldCall: { mutation: 'draftOrderCreate', input }, } } const data = await shopifyGraphql<{ draftOrderCreate: unknown }>({ shop: params.shop, apiVersion: params.apiVersion, query: `mutation CreateDraftOrder($input: DraftOrderInput!) { draftOrderCreate(input: $input) { draftOrder { id name status email createdAt updatedAt invoiceUrl totalPriceSet { ${MONEY_SET_FIELDS} } } userErrors { field message } } }`, variables: { input }, }) assertNoUserErrors(data.draftOrderCreate, 'draftOrderCreate') const created = ( data.draftOrderCreate as { draftOrder?: Record } ).draftOrder if (!created) throw new Error('draftOrderCreate returned no draft order.') return mapDraft(created) } /** Default export: list draft orders. */ export default listDraftOrders