import { clampInt, moneyFromSet, nodesFromConnection, pageInfoFromConnection, shopifyGraphql, trimString, } from './lib/client.ts' import { ORDER_FIELDS, PAGE_INFO_FIELDS } from './lib/fragments.ts' import { toGid } from './lib/shop.ts' import type { Money, ShopifyClientOptions, ShopifyPageInfo } from './types.ts' export type OrderLineItem = { id: string title: string | null sku: string | null quantity: number | null originalUnitPrice: Money | null } export type OrderSummary = { id: string name: string | null email: string | null createdAt: string | null updatedAt: string | null displayFinancialStatus: string | null displayFulfillmentStatus: string | null note: string | null tags: Array currentTotalPrice: Money | null customer: { id: string displayName: string | null email: string | null } | null lineItems: Array } export type ListOrdersParams = ShopifyClientOptions & { /** Shopify search query, e.g. `financial_status:paid fulfillment_status:unfulfilled`. */ query?: string first?: number after?: string } function mapLineItem(node: Record): OrderLineItem { return { id: String(node.id || ''), title: typeof node.title === 'string' ? node.title : null, sku: typeof node.sku === 'string' ? node.sku : null, quantity: typeof node.quantity === 'number' ? node.quantity : null, originalUnitPrice: moneyFromSet(node.originalUnitPriceSet), } } function mapOrder(node: Record): OrderSummary { const customer = node.customer && typeof node.customer === 'object' ? (node.customer as Record) : null return { id: String(node.id || ''), name: typeof node.name === 'string' ? node.name : 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, displayFinancialStatus: typeof node.displayFinancialStatus === 'string' ? node.displayFinancialStatus : null, displayFulfillmentStatus: typeof node.displayFulfillmentStatus === 'string' ? node.displayFulfillmentStatus : null, note: typeof node.note === 'string' ? node.note : null, tags: Array.isArray(node.tags) ? node.tags.map(String) : [], currentTotalPrice: moneyFromSet(node.currentTotalPriceSet), customer: customer ? { id: String(customer.id || ''), displayName: typeof customer.displayName === 'string' ? customer.displayName : null, email: typeof customer.email === 'string' ? customer.email : null, } : null, lineItems: nodesFromConnection>(node.lineItems).map( mapLineItem, ), } } /** * List orders with optional Shopify search syntax. * * @example * import { listOrders } from 'kody:@kody/shopify/orders' * const { items } = await listOrders({ * shop: 'acme', * query: 'fulfillment_status:unfulfilled', * first: 10, * }) */ export async function listOrders(params: ListOrdersParams = {}) { const first = clampInt(params.first, 1, 50, 20) const data = await shopifyGraphql<{ orders: unknown }>({ shop: params.shop, apiVersion: params.apiVersion, query: `query ListOrders($first: Int!, $after: String, $query: String) { orders(first: $first, after: $after, query: $query, sortKey: CREATED_AT, reverse: true) { ${PAGE_INFO_FIELDS} nodes { ${ORDER_FIELDS} } } }`, variables: { first, after: trimString(params.after) || null, query: trimString(params.query) || null, }, }) return { items: nodesFromConnection>(data.orders).map( mapOrder, ), pageInfo: pageInfoFromConnection(data.orders) as ShopifyPageInfo, } } /** * Get one order by GID or numeric id. */ export async function getOrder( params: ShopifyClientOptions & { id: string }, ): Promise { const id = toGid('Order', params.id) const data = await shopifyGraphql<{ order: Record | null }>({ shop: params.shop, apiVersion: params.apiVersion, query: `query GetOrder($id: ID!) { order(id: $id) { ${ORDER_FIELDS} } }`, variables: { id }, }) if (!data.order) throw new Error(`Order not found: ${id}`) return mapOrder(data.order) } /** Default export: list orders. */ export default listOrders