Edits history of script submission #22795 for ' New Invoices (coupa)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    import * as wmill from "windmill-client"
    
    type Invoice = { id: number; "created-at": string }
    
    /**
     * New Invoices
     * Trigger returning invoices created since the last run, tracked by a created-at watermark with id de-duplication.
     */
    export async function main(auth: RT.Coupa) {
      const base = auth.instance_url.replace(/\/+$/, "")
      const state: { watermark: string; seenIds: number[] } | null =
        await wmill.getState()
    
      if (!state || !state.watermark) {
        // First run: start 60s in the past (clock-skew tolerance), return no backlog
        const seed = new Date(Date.now() - 60_000)
          .toISOString()
          .replace("Z", "+00:00")
        await wmill.setState({ watermark: seed, seenIds: [] })
        return []
      }
    
      // gt_or_eq + id de-dup instead of gt, so invoices sharing the watermark second aren't skipped
      const returned: Invoice[] = []
      let offset = 0
      while (true) {
        const url = new URL(`${base}/api/invoices`)
        url.searchParams.append("created_at[gt_or_eq]", state.watermark)
        url.searchParams.append("limit", "50")
        url.searchParams.append("offset", String(offset))
        const response = await fetch(url, {
          headers: {
            Authorization: `Bearer ${auth.token}`,
            Accept: "application/json",
          },
        })
        if (!response.ok) {
          throw new Error(`${response.status} ${await response.text()}`)
        }
        const page = (await response.json()) as Invoice[]
        returned.push(...page)
        if (page.length < 50) break
        offset += 50
      }
    
      const seen = new Set(state.seenIds)
      const newInvoices = returned.filter((i) => !seen.has(i.id))
    
      if (newInvoices.length > 0) {
        // Advance the watermark to the newest Coupa-returned created-at; remember
        // the ids at that exact instant so the next gt_or_eq query can skip them
        const maxEpoch = Math.max(
          ...returned.map((i) => new Date(i["created-at"]).getTime())
        )
        const atMax = returned.filter(
          (i) => new Date(i["created-at"]).getTime() === maxEpoch
        )
        await wmill.setState({
          watermark: atMax[0]["created-at"],
          seenIds: atMax.map((i) => i.id),
        })
      }
      return newInvoices
    }
    

    Submitted by hugo989 4 hours ago