[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/translateItems.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InvoicedItem } from 'vtex.return-app'

import type { CatalogGQL } from '../clients/catalogGQL'

/**
 * @returns the most up to date SKU translations, if it exist
 */
export const translateItemName = async (
  id: string,
  originalName: string,
  catalogClient: CatalogGQL
) => {
  try {
    const skuName = await catalogClient.getSKUTranslation(id)
    const isLocalized = skuName && skuName !== originalName

    return isLocalized ? skuName : null
  } catch (error) {
    error.message = 'Error translating item name'
    throw error
  }
}

export function handleTranlateItems(
  items: InvoicedItem[],
  catalogClient: CatalogGQL
): Promise<InvoicedItem[]> {
  return Promise.all(
    items.map(async (item: InvoicedItem) => {
      return {
        ...item,
        localizedName: await translateItemName(
          item.id,
          item.name,
          catalogClient
        ),
      }
    })
  )
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/getCostumerEmail.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ClientProfileDetail } from '@vtex/clients'
import type { Logger } from '@vtex/api'
import { ResolverError } from '@vtex/api'

/**
 * Why not ALWAYS using email for userProfile?
 * userProfile is parsed from session cookie or created when a request is made using AuthCookie token.
 * When submitting a request via GraphQL IDE or calling the endpoint using APP-KEY and APP-TOKEN, we don't have the userProfile.
 * Also, we cannot use the email in the order because it might be masked.
 */
export const getCustomerEmail = (
  clientProfileData: ClientProfileDetail,
  {
    userProfile,
    appkey,
    inputEmail,
  }: {
    userProfile?: UserProfile
    appkey?: string
    /**
     * Email sent via args, either via GraphQL or request body in REST
     * @type {string}
     */
    inputEmail?: string | null
  },
  {
    logger,
  }: {
    logger: Logger
  }
): string => {
  const requesterIsStoreUser =
    clientProfileData.userProfileId === userProfile?.userId

  // when the requester is the owner of the order, we can use the email parsed from the session cookie
  if (userProfile && requesterIsStoreUser) return userProfile.email

  // Case: Request made by an admin user for a store user (e.g. via GraphQL IDE or endpoint using auth cookie)
  if (userProfile && userProfile.role === 'admin' && !requesterIsStoreUser) {
    if (!inputEmail) {
      throw new ResolverError(
        'Missing Store user email. Store user email is required when using admin user makes the request',
        400
      )
    }

    return inputEmail
  }

  // When the request is made via integration using the endpoint, store user email is a required param.
  if (appkey) {
    if (!inputEmail) {
      throw new ResolverError(
        'Missing Store user email. Store user email is required when using appkey',
        400
      )
    }

    return inputEmail
  }

  logger.error({
    message: 'Could not parse store user email',
    params: {
      customerOrderId: clientProfileData.userProfileId,
      appkey,
      inputEmail,
      userProfile,
    },
  })

  throw new ResolverError('Missing costumer email', 400)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/validatePaymentMethod.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { RefundPaymentDataInput, PaymentOptions } from 'vtex.return-app'
import { ResolverError } from '@vtex/api'

import { isValidIBANNumber } from './isValidIBANNumber'

export const validatePaymentMethod = (
  refundPaymentData: RefundPaymentDataInput,
  paymentSettings: PaymentOptions
) => {
  const {
    enablePaymentMethodSelection,
    allowedPaymentTypes,
    automaticallyRefundPaymentMethod,
  } = paymentSettings

  const { refundPaymentMethod, iban, accountHolderName } = refundPaymentData

  // When admin doesn't allow selection, PM request has to be sameAsPurchase
  if (
    !enablePaymentMethodSelection &&
    refundPaymentMethod !== 'sameAsPurchase'
  ) {
    throw new ResolverError(
      `Payment method ${refundPaymentMethod} is not allowed`,
      400
    )
  }

  if (refundPaymentMethod === 'sameAsPurchase') {
    if (typeof automaticallyRefundPaymentMethod !== 'boolean') {
      throw new ResolverError(
        `automaticallyRefundPaymentMethod field isn't set on settings. It has to be a boolean when refundPaymentMethod is sameAsPurchase`,
        500
      )
    }

    // sameAsPurchase isn't a field on allowedPaymentTypes. Return here to satisfy TS.
    return
  }

  // The PM in the request has to be true in the allowedPaymentTypes on the settings.
  if (!allowedPaymentTypes[refundPaymentMethod]) {
    throw new ResolverError(
      `Payment method ${refundPaymentMethod} is not allowed`,
      400
    )
  }

  // eslint-disable-next-line vtex/prefer-early-return
  if (refundPaymentMethod === 'bank') {
    if (!iban) {
      throw new ResolverError('IBAN is required', 400)
    }

    if (!accountHolderName) {
      throw new ResolverError('Account holder name is required', 400)
    }

    if (!isValidIBANNumber(iban)) {
      throw new ResolverError('IBAN is not valid', 400)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/transformOrderClientProfileData.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ClientProfileDetail } from '@vtex/clients'
import type { ClientProfileData } from 'vtex.return-app'

export const transformOrderClientProfileData = (
  clientProfileData: ClientProfileDetail,
  email: string
): ClientProfileData => {
  return {
    name: `${clientProfileData.firstName} ${clientProfileData.lastName}`,
    email,
    phoneNumber: clientProfileData.phone,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/createOrUpdateStatusPayload.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError } from '@vtex/api'
import type {
  Maybe,
  ReturnRequest,
  ReturnRequestCommentInput,
  Status,
} from 'vtex.return-app'

export const createOrUpdateStatusPayload = ({
  refundStatusData,
  requestStatus,
  comment,
  submittedBy,
  createdAt,
}: {
  refundStatusData: ReturnRequest['refundStatusData']
  requestStatus: Status
  comment: Maybe<ReturnRequestCommentInput> | undefined
  submittedBy: string
  createdAt: string
}): ReturnRequest['refundStatusData'] => {
  if (!refundStatusData) {
    throw new ResolverError('Request does not have refundStatusData')
  }

  const newComment = comment
    ? {
        comment: comment.value,
        createdAt,
        submittedBy,
        visibleForCustomer: Boolean(comment.visibleForCustomer),
        role: 'adminUser' as const,
      }
    : null

  const hasRefundStatus = refundStatusData.find(
    ({ status }) => status === requestStatus
  )

  const statusPayload = hasRefundStatus
    ? refundStatusData.map((statusObject) => {
        if (statusObject.status !== requestStatus) return statusObject
        const updatedStatusObject = {
          ...statusObject,
          comments: [
            ...(statusObject?.comments ?? []),
            ...(newComment ? [newComment] : []),
          ],
        }

        return updatedStatusObject
      })
    : [
        ...refundStatusData,
        {
          status: requestStatus,
          submittedBy,
          createdAt,
          comments: [...(newComment ? [newComment] : [])],
        },
      ]

  return statusPayload
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/canOrderBeReturned.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError } from '@vtex/api'

import { isWithinMaxDaysToReturn } from './dateHelpers'
import { ORDER_TO_RETURN_VALIDATON } from './constants'

const { ORDER_NOT_INVOICED, OUT_OF_MAX_DAYS } = ORDER_TO_RETURN_VALIDATON

export const canOrderBeReturned = ({
  creationDate,
  maxDays,
  status,
}: {
  creationDate: string
  maxDays: number
  status: string
}) => {
  if (!isWithinMaxDaysToReturn(creationDate, maxDays)) {
    throw new ResolverError(
      'Order is not within max days to return',
      400,
      OUT_OF_MAX_DAYS
    )
  }

  if (status !== 'invoiced') {
    throw new ResolverError('Order is not invoiced', 400, ORDER_NOT_INVOICED)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/createRefundData.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Maybe, RefundDataInput, ReturnRequest } from 'vtex.return-app'
import { UserInputError } from '@vtex/api'

export const createRefundData = ({
  requestId,
  refundData,
  requestItems,
  refundableShipping,
}: {
  requestId: string
  refundData?: Maybe<RefundDataInput>
  requestItems: ReturnRequest['items']
  refundableShipping: number
}): ReturnRequest['refundData'] => {
  const requestItemsMap = new Map<number, ReturnRequest['items'][number]>()

  for (const requestedItem of requestItems ?? []) {
    requestItemsMap.set(requestedItem.orderItemIndex as number, requestedItem)
  }

  const items = []

  for (const refundItem of refundData?.items ?? []) {
    if (refundItem.quantity === 0) continue
    const requestedItem = requestItemsMap.get(refundItem.orderItemIndex)

    if (!requestedItem) {
      throw new UserInputError(
        `Item index ${refundItem.orderItemIndex} isn't in the return request`
      )
    }

    if (requestedItem.quantity < refundItem.quantity) {
      throw new UserInputError(
        `Item index ${refundItem.orderItemIndex} has a quantity of ${requestedItem.quantity} but ${refundItem.quantity} was requested to return`
      )
    }

    const { orderItemIndex, sellingPrice, tax, id } = requestedItem

    if (typeof refundItem.restockFee !== 'number') {
      throw new UserInputError(
        `Item index ${refundItem.orderItemIndex} has a invalid restockFee. It must be a number. Pass 0 if there is no restock fee.`
      )
    }

    items.push({
      orderItemIndex,
      id,
      price: (Number(sellingPrice) ?? 0) + (Number(tax) ?? 0),
      quantity: refundItem.quantity,
      restockFee: refundItem.restockFee,
    })
  }

  const refundedItemsValue = items.reduce(
    (total, item) => total + (item.price * item.quantity - item.restockFee),
    0
  )

  if (typeof refundData?.refundedShippingValue !== 'number') {
    throw new UserInputError(
      'Missing refundedShippingValue or not a valid number'
    )
  }

  const refundedShippingValue = refundData?.refundedShippingValue ?? 0

  if (refundableShipping < refundedShippingValue) {
    throw new UserInputError(
      `Refundable shipping value (${refundableShipping}) is less than the shipping value sent (${refundedShippingValue})`
    )
  }

  return {
    // invoiceNumber has to match the requestId.
    // This values is used to filter the invoices created via Return app when calculating the items available to be returned.
    invoiceNumber: requestId,
    invoiceValue: refundedItemsValue + refundedShippingValue,
    refundedItemsValue,
    refundedShippingValue,
    items,
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/constants.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { OrderToReturnValidation } from 'vtex.return-app'

import type {
  ReturnRequestConfirmation,
  ReturnRequestStatusUpdate,
} from '../typings/mailClient'

export const SETTINGS_PATH = 'app-settings'

export const ORDER_TO_RETURN_VALIDATON: Record<
  OrderToReturnValidation,
  OrderToReturnValidation
> = {
  OUT_OF_MAX_DAYS: 'OUT_OF_MAX_DAYS',
  ORDER_NOT_INVOICED: 'ORDER_NOT_INVOICED',
}

export const OMS_RETURN_REQUEST_CONFIRMATION = (
  locale = 'en-GB'
): ReturnRequestConfirmation => `oms-return-request-confirmation_${locale}`
export const OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME = (
  locale = 'en-GB'
) => `[OMS] Return Request Confirmation_${locale}`

export const OMS_RETURN_REQUEST_STATUS_UPDATE = (
  locale = 'en-GB'
): ReturnRequestStatusUpdate => `oms-return-request-status-update_${locale}`
export const OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME = (
  locale = 'en-GB'
) => `[OMS] Return Request Status Update_${locale}`

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/canRefundCard.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { TransactionDetail } from '@vtex/clients'

export const canRefundCard = (
  orderTransactions: TransactionDetail[]
): boolean =>
  orderTransactions.some((transactions) =>
    transactions.payments.some((transaction) => transaction.lastDigits)
  )

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/dateHelpers.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const getCurrentDate = () => new Date().toISOString()

export const substractDays = (currentDate: string, maxDays: number) => {
  const date = new Date(currentDate)

  date.setDate(date.getDate() - maxDays)
  date.setUTCHours(0, 0, 0)

  return date.toISOString()
}

export const isWithinMaxDaysToReturn = (
  orderCreationDate: string,
  maxDays: number
) => {
  const currentDate = getCurrentDate()

  const limitDateToReturn = substractDays(currentDate, maxDays)

  return new Date(orderCreationDate) > new Date(limitDateToReturn)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/appSettingsValidation.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  PaymentOptionsInput,
  PaymentTypeInput,
  CustomReturnReasonInput,
  PaymentOptions,
} from 'vtex.return-app'

export const validatePaymentOptions = (
  paymentOptions: PaymentOptionsInput
): PaymentOptions => {
  const {
    enablePaymentMethodSelection,
    allowedPaymentTypes,
    automaticallyRefundPaymentMethod,
  } = paymentOptions

  // If the user has not enabled the payment method selection, then the allowed payment types can be any available. The automaticallyRefundPaymentMethod needs to have a value.
  if (!enablePaymentMethodSelection) {
    if (typeof automaticallyRefundPaymentMethod !== 'boolean') {
      throw new Error(
        'When payment method selection is disabled, the automaticallyRefundPaymentMethod needs to be a boolean'
      )
    }

    return paymentOptions
  }

  // Make automaticallyRefundPaymentMethod null when enablePaymentMethodSelection is true. This way we avoid confusion. We cannot have this value as true when payment method selection is eneble.
  const adjustedPaymentOptions = {
    ...paymentOptions,
    automaticallyRefundPaymentMethod: null,
  }

  for (const paymentType of Object.keys(allowedPaymentTypes)) {
    // If we have at least one payment method selected, then the payment options are valid.
    if (allowedPaymentTypes[paymentType as keyof PaymentTypeInput]) {
      return adjustedPaymentOptions
    }
  }

  // if we got here, all the alloed payment types are unselected.
  throw new Error('At least one payment method must be selected')
}

export const validateMaxDaysCustomReasons = (
  maxDays: number,
  customReturnReasons: CustomReturnReasonInput[] | undefined | null
) => {
  if (!customReturnReasons) return

  if (customReturnReasons.length === 0) return

  const maxCustomOptionsDays = customReturnReasons.reduce(
    (maxDay, option) => (maxDay > option.maxDays ? maxDay : option.maxDays),
    0
  )

  if (maxCustomOptionsDays && maxCustomOptionsDays < maxDays) {
    throw new Error(
      `No custom reason found having max days equal or greater than the general max days of ${maxDays}`
    )
  }

  for (const customReason of customReturnReasons) {
    if (customReason.maxDays > maxDays) {
      throw new Error(
        `Custom reason ${customReason.reason} cannot have a max days (${customReason.maxDays}) greater than the general max days ${maxDays}`
      )
    }
  }
}

export const valideteUniqueCustomReasonsPerLocale = (
  customReturnReasons: CustomReturnReasonInput[] | undefined | null
) => {
  if (!customReturnReasons) return

  if (customReturnReasons.length === 0) return

  for (const customReturn of customReturnReasons) {
    const { translations } = customReturn

    if (!translations) continue

    const uniqueLocales = new Set()

    for (const translation of translations) {
      if (uniqueLocales.has(translation.locale)) {
        throw new Error(
          `Reason ${customReturn.reason} has duplicate translation for locale ${translation.locale}`
        )
      }

      uniqueLocales.add(translation.locale)
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/createRefundableTotals.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { RefundableAmountTotal, ReturnRequest } from 'vtex.return-app'
import type { ItemTotal } from '@vtex/clients'

export const createRefundableTotals = (
  itemsToReturn: ReturnRequest['items'],
  totals: ItemTotal[],
  proportionalShippingSetting?: boolean | null
): RefundableAmountTotal[] => {
  const orderShippingTotal =
    totals.find(({ id }) => id === 'Shipping')?.value ?? 0

  const orderItemsTotal = totals.find(({ id }) => id === 'Items')?.value ?? 0
  const orderDiscountsTotal =
    totals.find(({ id }) => id === 'Discounts')?.value ?? 0

  const itemsAmount =
    itemsToReturn?.reduce((total, item) => {
      const { quantity, sellingPrice } = item

      return total + (quantity ?? 0) * (sellingPrice ?? 0)
    }, 0) ?? 0

  const itemsTotal = { id: 'items' as const, value: itemsAmount }

  const taxAmount =
    itemsToReturn?.reduce((total, item) => {
      const { quantity, tax } = item

      return total + (quantity ?? 0) * (tax ?? 0)
    }, 0) ?? 0

  const taxTotal = { id: 'tax' as const, value: taxAmount }

  const orderItemsValue = orderItemsTotal + orderDiscountsTotal

  // This handles the case where the items returned are all gifts (net value = 0)
  const areAllItemsGift = orderItemsValue === 0

  // If all items are gifts, we split the shipping value proportionally
  const proportionalShippingValue = areAllItemsGift
    ? orderShippingTotal / itemsToReturn.length
    : parseFloat(
        ((itemsAmount * orderShippingTotal) / orderItemsValue).toFixed(0)
      )

  const shippingAmount = proportionalShippingSetting
    ? proportionalShippingValue
    : orderShippingTotal

  const shippingTotal = { id: 'shipping' as const, value: shippingAmount }

  return [itemsTotal, shippingTotal, taxTotal]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/createOrdersToReturnSummary.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { OrderDetailResponse } from '@vtex/clients'
import type { MasterDataEntity } from '@vtex/clients/build/clients/masterData/MasterDataEntity'
import type {
  OrderToReturnSummary,
  InvoicedItem,
  ExcludedItem,
  ProcessedItem,
  ReturnAppSettings,
  ReturnRequest,
} from 'vtex.return-app'

import { getInvoicedItems } from './getInvoicedItems'
import { mapItemIndexAndQuantity } from './mapItemIndexAndQuantity'
import { transformOrderClientProfileData } from './transformOrderClientProfileData'
import { transformShippingData } from './transformShippingData'
import { canRefundCard } from './canRefundCard'
import type { CatalogGQL } from '../clients/catalogGQL'
import { handleTranlateItems } from './translateItems'

interface CreateOrdersToReturnSummarySetup {
  excludedCategories: ReturnAppSettings['excludedCategories']
  returnRequestClient: MasterDataEntity<ReturnRequest>
  catalogGQL: CatalogGQL
}

export const createOrdersToReturnSummary = async (
  order: OrderDetailResponse,
  email: string,
  {
    excludedCategories,
    returnRequestClient,
    catalogGQL,
  }: CreateOrdersToReturnSummarySetup
): Promise<OrderToReturnSummary> => {
  const { items, orderId, creationDate } = order

  const returnRequestSameOrder = await returnRequestClient.search(
    { page: 1, pageSize: 100 },
    ['items', 'refundData', 'refundPaymentData'],
    undefined,
    `orderId=${orderId} AND status <> cancelled`
  )

  const invoicesCreatedByReturnApp: string[] = []
  const committedItemsToReturn: Array<{ itemIndex: number; quantity: number }> =
    []

  for (const returnRequest of returnRequestSameOrder) {
    const { refundData, items: rmaItems } =
      (returnRequest as Pick<
        ReturnRequest,
        'items' | 'refundData' | 'refundPaymentData'
      >) ?? {}

    const { invoiceNumber } = refundData ?? {}

    /**
     * Colect all invoices created by the return app.
     * The app creates invoices type Input on object only when refunding a card.
     * It's necessary to remove all the invoices from OMS Order object to avoid considering the items twice.
     */
    // Use
    if (invoiceNumber) {
      invoicesCreatedByReturnApp.push(invoiceNumber)
    }

    for (const item of rmaItems ?? []) {
      const { orderItemIndex, quantity } = item

      if (orderItemIndex === undefined || quantity === undefined) continue

      const committedItem = {
        itemIndex: orderItemIndex,
        quantity,
      }

      committedItemsToReturn.push(committedItem)
    }
  }

  // get all items (based on index on order.items) that were invoiced and sent to customer.
  // By the documentation: The Output type should be used when the invoice you are sending is a selling invoice
  // https://developers.vtex.com/vtex-rest-api/reference/invoicenotification
  const outputInvoicedItems = getInvoicedItems(
    order.packageAttachment.packages,
    'Output'
  )

  // Keep only the invoices created outside the return app. Otherwise, an item could be cosidered twice.
  const invoicedOutsideRMAApp = order.packageAttachment.packages.filter(
    ({ invoiceNumber }) => {
      return !invoicesCreatedByReturnApp.includes(invoiceNumber)
    }
  )

  // get all items (based on index on order.items) that were send a return invoice.
  // By the documentation: The Input type should be used when you send a return invoice.
  // https://developers.vtex.com/vtex-rest-api/reference/invoicenotification
  const inputIvoicedItems = getInvoicedItems(invoicedOutsideRMAApp, 'Input')

  // Create a map to get quantity invoiced with Output type for an item (based on its index)
  const quantityInvoiced = mapItemIndexAndQuantity(outputInvoicedItems)

  // Create a map to get quantity invoiced with Input type for an item (based on its index)
  const returnedQuantityInvoiced = mapItemIndexAndQuantity(inputIvoicedItems)

  // Create a map to get quantity committed (added in other RMA) for an item (based on its index)
  const committedQuantityMap = mapItemIndexAndQuantity(committedItemsToReturn)

  const invoicedItems: InvoicedItem[] = []
  const excludedItems: ExcludedItem[] = []
  const processedItems: ProcessedItem[] = []

  // Associate itemIndex with the correspondent item in the order.item
  for (const [index, quantity] of quantityInvoiced.entries()) {
    const {
      id,
      productId,
      name,
      imageUrl,
      additionalInfo: { categoriesIds },
    } = items[index]

    // Here we can add the fields we want to have in the final item array. TBD the needed ones
    const currentLength = invoicedItems.push({
      id,
      productId,
      quantity,
      name,
      imageUrl,
      orderItemIndex: index,
    })

    const categoryIdList = categoriesIds.split('/').filter(Boolean)
    const excludedCategory = excludedCategories.filter((categoryId) =>
      categoryIdList.includes(categoryId)
    )

    // If item is in a category that is forbidden to be returned by admin, add it to the control array
    if (excludedCategory.length) {
      excludedItems.push({
        itemIndex: currentLength - 1,
        reason: {
          key: 'EXCLUDED_CATEGORY',
          value: JSON.stringify(excludedCategory),
        },
      })
    }

    // A item could have been refunded via RMA (committedQuantity) app or OMS interface or manually via API order/{orderId}/invoice (returnedQuantity)
    const returnedQuantity = returnedQuantityInvoiced.get(index) ?? 0
    const committedQuantity = committedQuantityMap.get(index) ?? 0

    const processedQuantity = returnedQuantity + committedQuantity

    if (processedQuantity) {
      processedItems.push({
        itemIndex: currentLength - 1,
        quantity: processedQuantity,
      })
    }
  }

  return {
    orderId,
    creationDate,
    invoicedItems: await handleTranlateItems(invoicedItems, catalogGQL),
    processedItems,
    excludedItems,
    clientProfileData: transformOrderClientProfileData(
      order.clientProfileData,
      email
    ),
    shippingData: transformShippingData(order.shippingData),
    paymentData: {
      canRefundCard: canRefundCard(order.paymentData.transactions),
    },
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/isValidIBANNumber.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
function mod97(string: string) {
  let checksum = string.slice(0, 2) as string | number
  let fragment

  for (let offset = 2; offset < string.length; offset += 7) {
    fragment = `${String(checksum)}${string.substring(offset, offset + 7)}`
    checksum = parseInt(fragment, 10) % 97
  }

  return checksum
}

export function isValidIBANNumber(input: string) {
  type CodeLengths = {
    [key: string]: number
  }

  const CODE_LENGTHS: CodeLengths = {
    AD: 24,
    AE: 23,
    AT: 20,
    AZ: 28,
    BA: 20,
    BE: 16,
    BG: 22,
    BH: 22,
    BR: 29,
    CH: 21,
    CR: 21,
    CY: 28,
    CZ: 24,
    DE: 22,
    DK: 18,
    DO: 28,
    EE: 20,
    ES: 24,
    FI: 18,
    FO: 18,
    FR: 27,
    GB: 22,
    GI: 23,
    GL: 18,
    GR: 27,
    GT: 28,
    HR: 21,
    HU: 28,
    IE: 22,
    IL: 23,
    IS: 26,
    IT: 27,
    JO: 30,
    KW: 30,
    KZ: 20,
    LB: 28,
    LI: 21,
    LT: 20,
    LU: 20,
    LV: 21,
    MC: 27,
    MD: 24,
    ME: 22,
    MK: 19,
    MR: 27,
    MT: 31,
    MU: 30,
    NL: 18,
    NO: 15,
    PK: 24,
    PL: 28,
    PS: 29,
    PT: 25,
    QA: 29,
    RO: 24,
    RS: 22,
    SA: 24,
    SE: 24,
    SI: 19,
    SK: 24,
    SM: 27,
    TN: 24,
    TR: 26,
    AL: 28,
    BY: 28,
    EG: 29,
    GE: 22,
    IQ: 23,
    LC: 32,
    SC: 31,
    ST: 25,
    SV: 28,
    TL: 23,
    UA: 29,
    VA: 22,
    VG: 24,
    XK: 20,
  }

  const iban = String(input)
    .toUpperCase()
    .replace(/[^A-Z0-9]/g, '')

  const code = iban.match(/^([A-Z]{2})(\d{2})([A-Z\d]+)$/)

  if (!code || iban.length !== CODE_LENGTHS[code[1]]) {
    return false
  }

  const dig = code[3] + code[1] + code[2]
  const digits = dig.replace(/[A-Z]/g, (letter) => {
    const returnVar = letter.charCodeAt(0) - 55

    return returnVar.toString()
  })

  // If the IBAN is correct the result should be 1 if not any number higher than 1.
  return mod97(digits) === 1
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/isUserAllowed.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ClientProfileDetail } from '@vtex/clients'
import { ForbiddenError, ResolverError } from '@vtex/api'

export const isUserAllowed = ({
  requesterUser,
  clientProfile,
  appkey,
}: {
  requesterUser?: UserProfile
  clientProfile: ClientProfileDetail
  appkey?: string
}) => {
  // If appkey is in the request, it means that the request was authenticated with appkey and apptoken
  if (appkey) return

  // if appkey doesn't exist, we need a UserProfile
  if (!requesterUser) {
    throw new ResolverError('Missing User Profile data')
  }

  const { userId, role } = requesterUser
  const { userProfileId } = clientProfile

  const orderBelongsToUser = userId === userProfileId
  const userIsAdmin = role === 'admin'

  // User should only be able to see / use their order.
  if (!orderBelongsToUser && !userIsAdmin) {
    throw new ForbiddenError('User cannot access this order')
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/validateStatusUpdate.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError, UserInputError } from '@vtex/api'
import type { Status } from 'vtex.return-app'

const statusAllowed: Record<Status, Status[]> = {
  new: ['new', 'processing', 'denied', 'cancelled'],
  processing: ['processing', 'pickedUpFromClient', 'denied', 'cancelled'],
  pickedUpFromClient: ['pickedUpFromClient', 'pendingVerification', 'denied'],
  pendingVerification: ['pendingVerification', 'packageVerified'],
  // In this step, when sending the items to the resolver, it will assign the status denied or packageVerified based on the items sent.
  packageVerified: ['packageVerified', 'amountRefunded'],
  amountRefunded: ['amountRefunded'],
  denied: ['denied'],
  cancelled: ['cancelled'],
}

export const validateStatusUpdate = (
  newStatus: Status,
  currentStatus: Status
) => {
  if (!newStatus) {
    throw new UserInputError('Missing status')
  }

  if (!statusAllowed[newStatus]) {
    throw new UserInputError(
      `Invalid status: ${newStatus}. Valid values: ${Object.keys(
        statusAllowed
      ).join(', ')}`
    )
  }

  if (!statusAllowed[currentStatus].includes(newStatus)) {
    throw new ResolverError(
      `Status transition from ${currentStatus} to ${newStatus} is not allowed. Valid status: ${statusAllowed[
        currentStatus
      ].join(', ')}`
    )
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/returnRequestList.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { QueryReturnRequestListArgs } from 'vtex.return-app'

import { returnRequestListService } from '../services/returnRequestListService'

export const returnRequestList = (
  _: unknown,
  args: QueryReturnRequestListArgs,
  ctx: Context
) => {
  return returnRequestListService(ctx, args)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/appSettings.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  ReturnAppSettings,
  MutationSaveReturnAppSettingsArgs,
} from 'vtex.return-app'

import {
  validateMaxDaysCustomReasons,
  validatePaymentOptions,
  valideteUniqueCustomReasonsPerLocale,
} from '../utils/appSettingsValidation'
import { SETTINGS_PATH } from '../utils/constants'

const returnAppSettings = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
): Promise<ReturnAppSettings | null> => {
  const {
    clients: { appSettings },
  } = ctx

  const settings = await appSettings.get(SETTINGS_PATH, true)

  if (!settings) return null

  return settings
}

const saveReturnAppSettings = async (
  _root: unknown,
  args: MutationSaveReturnAppSettingsArgs,
  ctx: Context
) => {
  const {
    clients: { appSettings },
  } = ctx

  // validate if all custom reasons have max days smaller than the general max days
  validateMaxDaysCustomReasons(
    args.settings.maxDays,
    args.settings.customReturnReasons
  )

  // validate if all custom reasons have unique locales for their translations
  valideteUniqueCustomReasonsPerLocale(args.settings.customReturnReasons)

  const settings = {
    ...args.settings,
    // validate that there is at least one payment method selected or user has to use the same as in the order
    paymentOptions: validatePaymentOptions(args.settings.paymentOptions),
  }

  await appSettings.save(SETTINGS_PATH, settings)

  return true
}

export const queries = { returnAppSettings }
export const mutations = { saveReturnAppSettings }

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/orderToReturnSummary.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError, UserInputError } from '@vtex/api'
import type { OrderToReturnSummary } from 'vtex.return-app'

import { SETTINGS_PATH } from '../utils/constants'
import { createOrdersToReturnSummary } from '../utils/createOrdersToReturnSummary'
import { isUserAllowed } from '../utils/isUserAllowed'
import { canOrderBeReturned } from '../utils/canOrderBeReturned'
import { getCustomerEmail } from '../utils/getCostumerEmail'

export const orderToReturnSummary = async (
  _: unknown,
  args: { orderId: string; storeUserEmail?: string },
  ctx: Context
): Promise<OrderToReturnSummary> => {
  const { orderId, storeUserEmail } = args
  const {
    state: { userProfile, appkey },
    clients: {
      appSettings,
      oms,
      returnRequest: returnRequestClient,
      catalogGQL,
    },
    vtex: { logger },
  } = ctx

  const settings = await appSettings.get(SETTINGS_PATH, true)

  if (!settings) {
    throw new ResolverError('Return App settings is not configured', 500)
  }

  const { maxDays, excludedCategories } = settings

  // For requests where orderId is an empty string
  if (!orderId) {
    throw new UserInputError('Order ID is missing')
  }

  const order = await oms.order(orderId)

  const { creationDate, clientProfileData, status } = order

  isUserAllowed({
    requesterUser: userProfile,
    clientProfile: clientProfileData,
    appkey,
  })

  canOrderBeReturned({
    creationDate,
    maxDays,
    status,
  })

  const customerEmail = getCustomerEmail(
    clientProfileData,
    {
      userProfile,
      appkey,
      inputEmail: storeUserEmail,
    },
    {
      logger,
    }
  )

  return createOrdersToReturnSummary(order, customerEmail, {
    excludedCategories,
    returnRequestClient,
    catalogGQL,
  })
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/nearestPickupPoints.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  QueryNearestPickupPointsArgs,
  NearPickupPointQueryResponse,
} from 'vtex.return-app'

export const nearestPickupPoints = async (
  _: unknown,
  { lat, long }: QueryNearestPickupPointsArgs,
  ctx: Context
): Promise<NearPickupPointQueryResponse> => {
  const {
    clients: { checkout },
  } = ctx

  const closestPickupPointsData = await checkout.getNearestPickupPoints(
    lat,
    long
  )

  return closestPickupPointsData
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/createReturnRequest.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { MutationCreateReturnRequestArgs } from 'vtex.return-app'

import { createReturnRequestService } from '../services/createReturnRequestService'

export const createReturnRequest = async (
  _: unknown,
  args: MutationCreateReturnRequestArgs,
  ctx: Context
) => {
  const { returnRequest } = args

  return createReturnRequestService(ctx, returnRequest)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/updateReturnRequestStatus.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  MutationUpdateReturnRequestStatusArgs,
  ReturnRequest,
} from 'vtex.return-app'

import { updateRequestStatusService } from '../services/updateRequestStatusService'

export const updateReturnRequestStatus = (
  _: unknown,
  args: MutationUpdateReturnRequestStatusArgs,
  ctx: Context
): Promise<ReturnRequest> => updateRequestStatusService(ctx, args)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/returnRequest.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { QueryReturnRequestArgs } from 'vtex.return-app'

import { returnRequestService } from '../services/returnRequestService'

export const returnRequest = async (
  _: unknown,
  { requestId }: QueryReturnRequestArgs,
  ctx: Context
) => {
  return returnRequestService(ctx, requestId)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/categoryTreeName.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { CategoryInfo } from 'vtex.return-app'

const transformCategoryTree = (
  categoryTree: CategoryTree[],
  parentName = ''
): CategoryInfo[] => {
  const categoryInfoFlatten = []

  for (const category of categoryTree) {
    if (!category.hasChildren) {
      categoryInfoFlatten.push({
        id: category.id,
        name: parentName ? `${parentName} > ${category.name}` : category.name,
      })
    } else {
      const categoryTreeName = parentName
        ? `${parentName} > ${category.name}`
        : category.name

      const group = transformCategoryTree(category.children, categoryTreeName)

      categoryInfoFlatten.push(...group, {
        id: category.id,
        name: categoryTreeName,
      })
    }
  }

  return categoryInfoFlatten
}

export const categoryTreeName = async (
  _: unknown,
  _args: unknown,
  ctx: Context
): Promise<CategoryInfo[]> => {
  const {
    clients: { catalog },
  } = ctx

  const categoryTree = await catalog.getCategoryTree()

  return transformCategoryTree(categoryTree)
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/ReturnRequestResponse.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ReturnRequest } from 'vtex.return-app'

type VtexProduct = 'admin' | 'store' | undefined

const removeSubmittedByForStoreUser = (
  statusData: ReturnRequest['refundStatusData'][number],
  vtexProduct: VtexProduct
) => {
  if (vtexProduct === 'store') {
    return {
      ...statusData,
      submittedBy: undefined,
    }
  }

  return statusData
}

// This function hides the submittedBy field and removes the comments that should not be visible to the store user
const transformStatusForStoreUser = (
  refundStatusDataList: ReturnRequest['refundStatusData'],
  vtexProduct: VtexProduct
) => {
  return (
    refundStatusDataList?.map((statusData) => {
      return {
        ...removeSubmittedByForStoreUser(statusData, vtexProduct),
        comments: statusData.comments?.filter((comment) => {
          return vtexProduct !== 'store' || comment.visibleForCustomer
        }),
      }
    }) ?? []
  )
}

// This resolver allows the parent one to fetch just the root fields for a ReturnRequest.
// This stategy can save some kb when transfering data, since that in a search, we don't need all the fields.
export const ReturnRequestResponse = {
  refundableAmount: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, refundableAmount } = root

    if (refundableAmount) return refundableAmount

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { refundableAmount: refundableAmountValue } =
      await returnRequestClient.get(id as string, ['refundableAmount'])

    return refundableAmountValue
  },
  customerProfileData: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, customerProfileData } = root

    if (customerProfileData) return customerProfileData

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { customerProfileData: customerProfile } =
      await returnRequestClient.get(id as string, ['customerProfileData'])

    return customerProfile
  },
  refundableAmountTotals: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, refundableAmountTotals } = root

    if (refundableAmountTotals) return refundableAmountTotals

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { refundableAmountTotals: refundableAmountTotalsData } =
      await returnRequestClient.get(id as string, ['refundableAmountTotals'])

    return refundableAmountTotalsData
  },
  pickupReturnData: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, pickupReturnData } = root

    if (pickupReturnData) return pickupReturnData

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { pickupReturnData: pickupData } = await returnRequestClient.get(
      id as string,
      ['pickupReturnData']
    )

    return pickupData
  },
  refundPaymentData: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, refundPaymentData } = root

    if (refundPaymentData) return refundPaymentData

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { refundPaymentData: refundData } = await returnRequestClient.get(
      id as string,
      ['refundPaymentData']
    )

    return refundData
  },
  items: async (root: ReturnRequest, _args: unknown, ctx: Context) => {
    const { id, items } = root

    if (items) return items

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { items: itemsList } = await returnRequestClient.get(id as string, [
      'items',
    ])

    return itemsList
  },
  refundData: async (root: ReturnRequest, _args: unknown, ctx: Context) => {
    const { id, refundData } = root

    if (refundData) return refundData

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { refundData: refundDataList } = await returnRequestClient.get(
      id as string,
      ['refundData']
    )

    return refundDataList
  },
  refundStatusData: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, refundStatusData } = root
    const {
      clients: { returnRequest: returnRequestClient },
      request: { header },
    } = ctx

    const vtexProduct = header['x-vtex-product'] as VtexProduct

    const { refundStatusData: refundStatusDataList } = refundStatusData
      ? { refundStatusData }
      : await returnRequestClient.get(id as string, ['refundStatusData'])

    return transformStatusForStoreUser(refundStatusDataList, vtexProduct)
  },
  cultureInfoData: async (
    root: ReturnRequest,
    _args: unknown,
    ctx: Context
  ) => {
    const { id, cultureInfoData } = root

    if (cultureInfoData) return cultureInfoData

    const {
      clients: { returnRequest: returnRequestClient },
    } = ctx

    const { cultureInfoData: cultureInfo } = await returnRequestClient.get(
      id as string,
      ['cultureInfoData']
    )

    return cultureInfo
  },
  // Resolve dateSubmitted value into createdIn field because we lost the original value of createdIn (migration data from v2 to v3).
  createdIn: async (root: ReturnRequest) => {
    const { dateSubmitted } = root

    return dateSubmitted
  },
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/ordersAvailableToReturn.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError } from '@vtex/api'
import type { OrdersToReturnList, OrderToReturnSummary } from 'vtex.return-app'

import { SETTINGS_PATH } from '../utils/constants'
import { createOrdersToReturnSummary } from '../utils/createOrdersToReturnSummary'
import { getCurrentDate, substractDays } from '../utils/dateHelpers'

const ONE_MINUTE = 60 * 1000

function pacer(callsPerMinute: number) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('done')
    }, ONE_MINUTE / callsPerMinute)
  })
}

const createParams = ({
  maxDays,
  userEmail,
  page = 1,
}: {
  maxDays: number
  userEmail: string
  page: number
}) => {
  const currentDate = getCurrentDate()

  return {
    clientEmail: userEmail,
    orderBy: 'creationDate,desc' as const,
    f_status: 'invoiced' as const,
    f_creationDate: `creationDate:[${substractDays(
      currentDate,
      maxDays
    )} TO ${currentDate}]`,
    page,
    per_page: 10 as const,
  }
}

export const ordersAvailableToReturn = async (
  _: unknown,
  args: { page: number; storeUserEmail?: string },
  ctx: Context
): Promise<OrdersToReturnList> => {
  const {
    state: { userProfile },
    clients: {
      appSettings,
      oms,
      returnRequest: returnRequestClient,
      catalogGQL,
    },
  } = ctx

  const { page, storeUserEmail } = args

  const settings = await appSettings.get(SETTINGS_PATH, true)

  if (!settings) {
    throw new ResolverError('Return App settings is not configured')
  }

  const { maxDays, excludedCategories } = settings
  const { email } = userProfile ?? {}

  const userEmail = storeUserEmail ?? email

  if (!userEmail) {
    throw new ResolverError('Missing user email', 400)
  }

  // Fetch order associated to the user email
  const { list, paging } = await oms.listOrdersWithParams(
    createParams({ maxDays, userEmail, page })
  )

  const orderListPromises = []

  for (const order of list) {
    // Fetch order details to get items and packages
    const orderPromise = oms.order(order.orderId)

    orderListPromises.push(orderPromise)

    // eslint-disable-next-line no-await-in-loop
    await pacer(2000)
  }

  const orders = await Promise.all(orderListPromises)

  const orderSummaryPromises: Array<Promise<OrderToReturnSummary>> = []

  for (const order of orders) {
    const orderToReturnSummary = createOrdersToReturnSummary(order, userEmail, {
      excludedCategories,
      returnRequestClient,
      catalogGQL,
    })

    orderSummaryPromises.push(orderToReturnSummary)
  }

  const orderList = await Promise.all(orderSummaryPromises)

  return { list: orderList, paging }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/resolvers/index.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { createReturnRequest } from './createReturnRequest'
import {
  queries as settingsQuery,
  mutations as settingsMutation,
} from './appSettings'
import { categoryTreeName } from './categoryTreeName'
import { ordersAvailableToReturn } from './ordersAvailableToReturn'
import { orderToReturnSummary } from './orderToReturnSummary'
import { returnRequest } from './returnRequest'
import { returnRequestList } from './returnRequestList'
import { ReturnRequestResponse } from './ReturnRequestResponse'
import { updateReturnRequestStatus } from './updateReturnRequestStatus'
import { nearestPickupPoints } from './nearestPickupPoints'

export const mutations = {
  createReturnRequest,
  updateReturnRequestStatus,
  ...settingsMutation,
}

export const queries = {
  ...settingsQuery,
  categoryTreeName,
  ordersAvailableToReturn,
  orderToReturnSummary,
  returnRequest,
  returnRequestList,
  nearestPickupPoints,
}

export const resolvers = { ReturnRequestResponse }

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/__tests__/simple.test.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
// You can import your files here for testing them
//
// More info: https://github.com/sapegin/jest-cheat-sheet

describe('simpleTest', () => {
  it('one test case', async () => {
    expect(0.2 + 0.1).not.toBe(0.3)
  })
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/services/createReturnRequestService.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { ReturnRequestCreated, ReturnRequestInput } from 'vtex.return-app'
import { UserInputError, ResolverError } from '@vtex/api'
import type { DocumentResponse } from '@vtex/clients/build/clients/masterData/MasterDataEntity'

import {
  SETTINGS_PATH,
  OMS_RETURN_REQUEST_CONFIRMATION,
} from '../utils/constants'
import { isUserAllowed } from '../utils/isUserAllowed'
import { canOrderBeReturned } from '../utils/canOrderBeReturned'
import { canReturnAllItems } from '../utils/canReturnAllItems'
import { validateReturnReason } from '../utils/validateReturnReason'
import { validatePaymentMethod } from '../utils/validatePaymentMethod'
import { validateCanUsedropoffPoints } from '../utils/validateCanUseDropoffPoints'
import { createItemsToReturn } from '../utils/createItemsToReturn'
import { createRefundableTotals } from '../utils/createRefundableTotals'
import { OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE } from '../utils/templates'
import type { ConfirmationMailData } from '../typings/mailClient'
import { getCustomerEmail } from '../utils/getCostumerEmail'
import { validateItemCondition } from '../utils/validateItemCondition'

export const createReturnRequestService = async (
  ctx: Context,
  args: ReturnRequestInput
): Promise<ReturnRequestCreated> => {
  const {
    clients: {
      oms,
      returnRequest: returnRequestClient,
      appSettings,
      mail,
      catalogGQL,
    },
    state: { userProfile, appkey },
    vtex: { logger },
  } = ctx

  const {
    orderId,
    items,
    customerProfileData,
    pickupReturnData,
    refundPaymentData,
    userComment,
    locale,
  } = args

  if (!appkey && !userProfile) {
    throw new ResolverError('Missing appkey or userProfile')
  }

  const { firstName, lastName, email } = userProfile ?? {}

  const submittedByNameOrEmail =
    firstName || lastName ? `${firstName} ${lastName}` : email

  // If request was validated using appkey and apptoken, we assign the appkey as a sender
  // Otherwise, we try to use requester name. Email is the last resort.
  const submittedBy = appkey ?? submittedByNameOrEmail

  if (!submittedBy) {
    throw new ResolverError(
      'Unable to get submittedBy from context. The request is missing the userProfile info or the appkey'
    )
  }

  const requestDate = new Date().toISOString()

  // Check items since a request via endpoint might not have it.
  // Graphql validation doesn't prevent user to send empty items
  if (!items || items.length === 0) {
    throw new UserInputError('There are no items in the request')
  }

  // For requests where orderId is an empty string
  if (!orderId) {
    throw new UserInputError('Order ID is missing')
  }

  const orderPromise = oms.order(orderId, 'AUTH_TOKEN')

  const searchRMAPromise = returnRequestClient.searchRaw(
    { page: 1, pageSize: 1 },
    ['id'],
    undefined,
    `orderId=${orderId}`
  )

  const settingsPromise = appSettings.get(SETTINGS_PATH, true)

  // If order doesn't exist, it throws an error and stop the process.
  // If there is no request created for that order, request searchRMA will be an empty array.
  const [order, searchRMA, settings] = await Promise.all([
    orderPromise,
    searchRMAPromise,
    settingsPromise,
  ])

  if (!settings) {
    throw new ResolverError('Return App settings is not configured', 500)
  }

  const {
    pagination: { total },
  } = searchRMA

  const {
    sequence,
    clientProfileData,
    items: orderItems,
    totals,
    creationDate,
    status,
    sellers,
    // @ts-expect-error itemMetadata is not typed in the OMS client project
    itemMetadata,
    shippingData,
    storePreferencesData: { currencyCode },
  } = order

  const {
    maxDays,
    excludedCategories,
    customReturnReasons,
    paymentOptions,
    options: settingsOptions,
  } = settings

  isUserAllowed({
    requesterUser: userProfile,
    clientProfile: clientProfileData,
    appkey,
  })

  canOrderBeReturned({
    creationDate,
    maxDays,
    status,
  })

  // Validate if all items are available to be returned
  await canReturnAllItems(items, {
    order,
    excludedCategories,
    returnRequestClient,
    catalogGQL,
  })

  // Validate maxDays for custom reasons.
  validateReturnReason(items, creationDate, customReturnReasons)

  // Validate payment methods
  validatePaymentMethod(refundPaymentData, paymentOptions)

  // validate address type
  validateCanUsedropoffPoints(
    pickupReturnData,
    settingsOptions?.enablePickupPoints
  )

  // validate item condition
  validateItemCondition(items, settingsOptions?.enableSelectItemCondition)

  // Possible bug here: If someone deletes a request, it can lead to a duplicated sequence number.
  // Possible alternative: Save a key value pair in to VBase where key is the orderId and value is either the latest sequence (as number) or an array with all Ids, so we can use the length to calcualate the next seuqence number.
  const sequenceNumber = `${sequence}-${total + 1}`

  const itemsToReturn = await createItemsToReturn({
    itemsToReturn: items,
    orderItems,
    sellers,
    itemMetadata,
    catalogGQL,
  })

  const refundableAmountTotals = createRefundableTotals(
    itemsToReturn,
    totals,
    settings?.options?.enableProportionalShippingValue
  )

  const refundableAmount = refundableAmountTotals.reduce(
    // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
    (amount, cur) => amount + cur.value,
    0
  )

  const userCommentData = userComment
    ? [
        {
          comment: userComment,
          createdAt: requestDate,
          submittedBy,
          visibleForCustomer: true,
          role: 'storeUser' as const,
        },
      ]
    : []

  // customerProfileData can be undefined when coming from a endpoint request
  const { email: inputEmail } = customerProfileData ?? {}

  const customerEmail = getCustomerEmail(
    clientProfileData,
    {
      userProfile,
      appkey,
      inputEmail,
    },
    {
      logger,
    }
  )

  const { refundPaymentMethod } = refundPaymentData

  const { iban, accountHolderName, ...refundPaymentMethodSubset } =
    refundPaymentData

  const refundPaymentDataResult =
    refundPaymentMethod === 'bank'
      ? refundPaymentData
      : refundPaymentMethodSubset

  const { automaticallyRefundPaymentMethod } = paymentOptions

  const createInvoiceTypeInput =
    refundPaymentMethod === 'sameAsPurchase'
      ? Boolean(automaticallyRefundPaymentMethod)
      : null

  let rmaDocument: DocumentResponse

  try {
    rmaDocument = await returnRequestClient.save({
      orderId,
      refundableAmount,
      sequenceNumber,
      status: 'new',
      refundableAmountTotals,
      customerProfileData: {
        userId: clientProfileData.userProfileId,
        name: customerProfileData.name,
        email: customerEmail,
        phoneNumber: customerProfileData.phoneNumber,
      },
      pickupReturnData,
      refundPaymentData: {
        ...refundPaymentDataResult,
        automaticallyRefundPaymentMethod: createInvoiceTypeInput,
      },
      items: itemsToReturn,
      dateSubmitted: requestDate,
      refundData: null,
      refundStatusData: [
        {
          status: 'new',
          submittedBy,
          createdAt: requestDate,
          comments: userCommentData,
        },
      ],
      cultureInfoData: {
        currencyCode,
        locale,
      },
    })
  } catch (error) {
    const mdValidationErrors = error?.response?.data?.errors[0]?.errors

    const errorMessageString = mdValidationErrors
      ? JSON.stringify(
          {
            message: 'Schema Validation error',
            errors: mdValidationErrors,
          },
          null,
          2
        )
      : error.message

    throw new ResolverError(errorMessageString, error.response?.status || 500)
  }

  // We add a try/catch here so we avoid sending an error to the browser only if the email fails.
  try {
    const templateExists = await mail.getTemplate(
      OMS_RETURN_REQUEST_CONFIRMATION(locale)
    )

    if (!templateExists) {
      await mail.publishTemplate(
        OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE(locale)
      )
    }

    const {
      firstName: clientFirstName,
      lastName: clientLastName,
      phone,
    } = clientProfileData

    const {
      address: { country, city, street },
    } = shippingData

    const mailData: ConfirmationMailData = {
      templateName: OMS_RETURN_REQUEST_CONFIRMATION(locale),
      jsonData: {
        data: {
          status: 'new',
          name: `${clientFirstName} ${clientLastName}`,
          DocumentId: rmaDocument.DocumentId,
          email: customerEmail,
          phoneNumber: phone,
          country,
          locality: city,
          address: street,
          paymentMethod: refundPaymentData.refundPaymentMethod,
        },
        products: [...itemsToReturn],
        refundStatusData: [
          {
            status: 'new',
            submittedBy,
            createdAt: requestDate,
            comments: userCommentData,
          },
        ],
      },
    }

    await mail.sendMail(mailData)
  } catch (error) {
    logger.warn({
      message: `Failed to send email for return request ${rmaDocument.DocumentId}`,
      error,
    })
  }

  return { returnRequestId: rmaDocument.DocumentId }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/services/updateRequestStatusService.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  MutationUpdateReturnRequestStatusArgs,
  ReturnRequest,
  Status,
  RefundItemInput,
} from 'vtex.return-app'
import {
  ResolverError,
  ForbiddenError,
  NotFoundError,
  UserInputError,
} from '@vtex/api'

import { validateStatusUpdate } from '../utils/validateStatusUpdate'
import { createOrUpdateStatusPayload } from '../utils/createOrUpdateStatusPayload'
import { createRefundData } from '../utils/createRefundData'
import { handleRefund } from '../utils/handleRefund'
import { OMS_RETURN_REQUEST_STATUS_UPDATE } from '../utils/constants'
import { OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE } from '../utils/templates'
import type { StatusUpdateMailData } from '../typings/mailClient'

// A partial update on MD requires all required field to be sent. https://vtex.slack.com/archives/C8EE14F1C/p1644422359807929
// And the request to update fails when we pass the auto generated ones.
// If any new field is added to the ReturnRequest as required, it has to be added here too.
const formatRequestToPartialUpdate = (
  request: ReturnRequest
): ReturnRequest => {
  const {
    orderId,
    refundableAmount,
    sequenceNumber,
    status,
    customerProfileData,
    pickupReturnData,
    refundPaymentData,
    items,
    refundData,
    refundableAmountTotals,
    refundStatusData,
    cultureInfoData,
    dateSubmitted,
  } = request

  const partialUpdate = {
    orderId,
    refundableAmount,
    sequenceNumber,
    status,
    customerProfileData,
    pickupReturnData,
    refundPaymentData,
    items,
    refundData,
    refundableAmountTotals,
    refundStatusData,
    cultureInfoData,
    dateSubmitted,
  }

  return partialUpdate
}

const acceptOrDenyPackage = (refundItemList?: RefundItemInput[]) => {
  if (!refundItemList) {
    throw new UserInputError(
      'Missing items inside refundData object. It is necessary to pass a list of items to refund. To deny all items, pass a empty array.'
    )
  }

  if (!Array.isArray(refundItemList)) {
    throw new UserInputError(
      'Item has to be an array. To deny all items, pass a empty array.'
    )
  }

  return refundItemList.some(({ quantity, orderItemIndex }) => {
    if (typeof quantity !== 'number') {
      throw new UserInputError(
        `Not a valid quantity for item index ${orderItemIndex}`
      )
    }

    return quantity > 0
  })
    ? 'packageVerified'
    : 'denied'
}

export const updateRequestStatusService = async (
  ctx: Context,
  args: MutationUpdateReturnRequestStatusArgs
): Promise<ReturnRequest> => {
  const {
    state: { userProfile, appkey },
    clients: {
      returnRequest: returnRequestClient,
      oms,
      giftCard: giftCardClient,
      mail,
    },
    vtex: { logger },
  } = ctx

  const { status, requestId, comment, refundData } = args

  const { role, firstName, lastName, email, userId } = userProfile ?? {}

  const requestDate = new Date().toISOString()
  const submittedByNameOrEmail =
    firstName || lastName ? `${firstName} ${lastName}` : email

  const submittedBy = appkey ?? submittedByNameOrEmail

  if (!submittedBy) {
    throw new ResolverError(
      'Unable to get submittedBy from context. The request is missing the userProfile info or the appkey'
    )
  }

  const returnRequest = (await returnRequestClient.get(requestId, [
    '_all',
  ])) as ReturnRequest

  if (!returnRequest) {
    throw new NotFoundError(`Request ${requestId} not found`)
  }

  const userIsAdmin = Boolean(appkey) || role === 'admin'

  const belongsToStoreUser =
    returnRequest.customerProfileData.userId === userId &&
    returnRequest.status === 'new'

  if (!userIsAdmin && !belongsToStoreUser) {
    throw new ForbiddenError('Not authorized')
  }

  validateStatusUpdate(status, returnRequest.status as Status)

  // when a request is made for the same status, it means admin user is adding a new comment
  if (status === returnRequest.status && !comment) {
    throw new UserInputError(
      'Missing comment. Comment is needed when status sent is equal the current status.'
    )
  }

  const isPackageVerified = status === 'packageVerified'

  // This is need in case a user wants to add a comment when status is packageVerified.
  // It avoids recreating a new refundData object and updating the request status
  const createRefundInvoice = isPackageVerified && !returnRequest.refundData

  if (createRefundInvoice && !refundData) {
    throw new UserInputError(
      'Missing refundData property. To update status to packageVerified it is necessary to send items verification object.'
    )
  }

  // When status is packageVerified, the final status is based on the quantity of items. If none is approved, status is denied.
  const requestStatus = createRefundInvoice
    ? acceptOrDenyPackage(refundData?.items)
    : status

  const refundStatusData = createOrUpdateStatusPayload({
    refundStatusData: returnRequest.refundStatusData,
    requestStatus,
    comment,
    submittedBy,
    createdAt: requestDate,
  })

  const maxRefundableShipping =
    returnRequest.refundableAmountTotals.find(({ id }) => id === 'shipping')
      ?.value ?? 0

  const refundInvoice =
    createRefundInvoice && requestStatus !== 'denied'
      ? createRefundData({
          requestId,
          refundData,
          requestItems: returnRequest.items,
          refundableShipping: maxRefundableShipping,
        })
      : returnRequest.refundData

  const refundReturn = await handleRefund({
    currentStatus: requestStatus,
    previousStatus: returnRequest.status,
    refundPaymentData: returnRequest.refundPaymentData ?? {},
    orderId: returnRequest.orderId as string,
    createdAt: requestDate,
    refundInvoice,
    userEmail: returnRequest.customerProfileData?.email as string,
    clients: {
      omsClient: oms,
      giftCardClient,
    },
  })

  const giftCard = refundReturn?.giftCard

  const updatedRequest = {
    ...formatRequestToPartialUpdate(returnRequest),
    status: requestStatus,
    refundStatusData,
    refundData: refundInvoice
      ? { ...refundInvoice, ...(giftCard ? { giftCard } : null) }
      : null,
  }

  try {
    await returnRequestClient.update(requestId, updatedRequest)
  } catch (error) {
    const mdValidationErrors = error?.response?.data?.errors[0]?.errors

    const errorMessageString = mdValidationErrors
      ? JSON.stringify(
          {
            message: 'Schema Validation error',
            errors: mdValidationErrors,
          },
          null,
          2
        )
      : error.message

    throw new ResolverError(errorMessageString, error.response?.status || 500)
  }

  const { cultureInfoData } = updatedRequest

  // We add a try/catch here so we avoid sending an error to the browser only if the email fails.
  try {
    const templateExists = await mail.getTemplate(
      OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale)
    )

    if (!templateExists) {
      await mail.publishTemplate(
        OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE(cultureInfoData?.locale)
      )
    }

    const {
      status: updatedStatus,
      items,
      customerProfileData,
      refundStatusData: updatedRefundStatusData,
      refundPaymentData,
      refundData: updatedRefundData,
    } = updatedRequest

    const mailData: StatusUpdateMailData = {
      templateName: OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale),
      jsonData: {
        data: {
          status: updatedStatus,
          name: customerProfileData?.name ?? '',
          DocumentId: requestId,
          email: customerProfileData?.email ?? '',
          paymentMethod: refundPaymentData?.refundPaymentMethod ?? '',
          iban: refundPaymentData?.iban ?? '',
          refundedAmount:
            Number(updatedRefundData?.refundedItemsValue) +
            Number(updatedRefundData?.refundedShippingValue),
        },
        products: items,
        refundStatusData: updatedRefundStatusData,
      },
    }

    await mail.sendMail(mailData)
  } catch (error) {
    logger.warn({
      message: `Failed to send email for return request ${requestId}`,
      error,
    })
  }

  return { id: requestId, ...updatedRequest }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/services/returnRequestListService.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  QueryReturnRequestListArgs,
  ReturnRequestFilters,
  Maybe,
} from 'vtex.return-app'
import { ForbiddenError } from '@vtex/api'

const filterDate = (date: string): string => {
  const newDate = new Date(date)
  const day = newDate.getDate()
  const month = newDate.getMonth() + 1
  const year = newDate.getFullYear()

  return `${year}-${month < 10 ? `0${month}` : `${month}`}-${
    day < 10 ? `0${day}` : `${day}`
  }`
}

const buildWhereClause = (filter: Maybe<ReturnRequestFilters> | undefined) => {
  if (!filter) return

  const returnFilters = Object.entries(filter)
  const whereFilter = returnFilters.reduce((where, [key, value]) => {
    if (!value) return where

    if (where.length) {
      where += ` AND `
    }

    if (key === 'userId') {
      where += `customerProfileData.userId = "${value}"`

      return where
    }

    if (key === 'userEmail') {
      where += `customerProfileData.email = "${value}"`

      return where
    }

    if (key === 'createdIn' && typeof value !== 'string') {
      where += `dateSubmitted between ${filterDate(
        value.from
      )} AND ${filterDate(value.to)}`

      return where
    }

    where += `${key}=${value}`

    return where
  }, '')

  return whereFilter
}

export const returnRequestListService = async (
  ctx: Context,
  args: QueryReturnRequestListArgs,
  getAllFields = false
) => {
  const {
    clients: { returnRequest: returnRequestClient },
    request: { header },
    state: { userProfile, appkey },
  } = ctx

  const { page, perPage, filter } = args
  const {
    userId: userIdProfile,
    email: userEmailProfile,
    role,
  } = userProfile ?? {}

  const { userId: userIdArg, userEmail: userEmailArg } = filter ?? {}

  const userIsAdmin = Boolean(appkey) || role === 'admin'

  // only admin users can pass userId or userEmail in the request.
  // For non admin users, the userId or userEmail must be gotten from cookie session.
  // Otherwise, a non admin user could search for another user's return requests
  const userId = userIsAdmin ? userIdArg || userIdProfile : userIdProfile
  const userEmail = userIsAdmin
    ? userEmailArg || userEmailProfile
    : userEmailProfile

  // vtexProduct is undefined when coming from GraphQL IDE or from a external request
  const vtexProduct = header['x-vtex-product'] as 'admin' | 'store' | undefined

  // When the user is not admin or the request is coming from the store, we need to apply the user filter to get the right requests
  const requireFilterByUser =
    !userIsAdmin || vtexProduct === 'store' || role === 'store-user'

  const hasUserIdOrEmail = Boolean(userId || userEmail)

  if (requireFilterByUser && !hasUserIdOrEmail) {
    throw new ForbiddenError('Missing params to filter by store user')
  }

  const adjustedFilter = requireFilterByUser
    ? { ...filter, userId, userEmail }
    : filter

  const resultFields = getAllFields
    ? ['_all']
    : [
        'id',
        'orderId',
        'sequenceNumber',
        'createdIn',
        'status',
        'dateSubmitted',
      ]

  const rmaSearchResult = await returnRequestClient.searchRaw(
    {
      page,
      pageSize: perPage && perPage <= 100 ? perPage : 25,
    },
    resultFields,
    'dateSubmitted DESC',
    buildWhereClause(adjustedFilter)
  )

  const { data, pagination } = rmaSearchResult
  const { page: currentPage, pageSize, total } = pagination

  return {
    list: data,
    paging: {
      total,
      perPage: pageSize,
      currentPage,
      pages: Math.ceil(total / pageSize),
    },
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/services/returnRequestService.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ResolverError, ForbiddenError } from '@vtex/api'
import type { ReturnRequest } from 'vtex.return-app'

export const returnRequestService = async (ctx: Context, requestId: string) => {
  const {
    clients: { returnRequest: returnRequestClient },
    state: { userProfile, appkey },
  } = ctx

  const { userId, role } = userProfile ?? {}
  const userIsAdmin = Boolean(appkey) || role === 'admin'

  const returnRequestResult = await returnRequestClient.get(requestId, ['_all'])

  if (!returnRequestResult) {
    // Code error 'E_HTTP_404' to match the one when failing to find and order by OMS
    throw new ResolverError(`Request ${requestId} not found`, 404, 'E_HTTP_404')
  }

  const { customerProfileData } = returnRequestResult as ReturnRequest

  const requestBelongsToUser = userId === customerProfileData?.userId

  if (!requestBelongsToUser && !userIsAdmin) {
    throw new ForbiddenError('User cannot access this request')
  }

  return returnRequestResult
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/templates/templateMessages.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import html from 'html-template-tag'

const HEAD_AND_STYLE = html`
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{_accountInfo.TradingName}}</title>
    <!--[if gte mso 9]>
      <xml>
        <o:OfficeDocumentSettings>
          <o:AllowPNG />
          <o:PixelsPerInch>96</o:PixelsPerInch>
        </o:OfficeDocumentSettings>
      </xml>
    <![endif]-->

  </head>
`

const HEADER = html`
  <tr style="box-sizing: border-box !important;">
    <td class="ph4-ns"
        style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; width: 100%; padding-bottom: 2rem; text-align: center !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
        align="center">
        <div style="box-sizing: border-box; width: 8rem; margin-bottom: 1rem; margin-top: 2rem; margin-right: auto; margin-left: auto !important;">
            <a href="http://{{_accountInfo.HostName}}.com.br"
                style="box-sizing: border-box !important;">
                <img alt="" border="0" width="auto"
                        src="http://licensemanager.vtex.com.br/api/site/pub/accounts/{{_accountInfo.Id}}/logos/show"
                        style="vertical-align: top; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; max-width: 100%; border: none; max-height: 80px !important;">
                </img>
            </a>
        </div>
        <h1 style="margin: 0; font-size: 25px; line-height: 35px; box-sizing: border-box !important;">
            {{#if data.status}}
            {{#compare data.status "==" 'new'}}
            New return request <br/>{{data.DocumentId}}
            {{else}}
            Return request <br/>{{data.DocumentId}}
            {{/compare}}
            {{/if}}
        </h1>
    </td>
  </tr>
`

const PRODUCTS = html`
  {{#compare products.length ">" 0}}

  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <h3
        style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
      >
        Products
      </h3>

      <table style="border-collapse:collapse;font-size:14px;width: 36rem">
        <thead style="text-align:left">
          <tr>
            <th>Products</th>
            <th>Quantity</th>
            <th>Unit price</th>
          </tr>

          <tr></tr>
        </thead>

        <tbody>
          {{#each products}} {{#compare quantity '>' 0}}
          <tr>
            <td>{{name}}</td>
            <td>{{quantity}}</td>
            <td>{{formatCurrency sellingPrice}}</td>
          </tr>
          {{/compare}} {{/each}}
        </tbody>
      </table>
    </td>
  </tr>

  {{/compare}}
`

const CONTACT_DETAILS = html`
  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <table style="border-collapse:collapse;font-size:14px;width: 36rem">
        <tr>
          <td>
            <h3
              style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
            >
              Contact Details
            </h3>
            <div>
              Name: {{data.name}}<br />
              E-mail: {{data.email}}<br />
              Phone: {{data.phoneNumber}}
            </div>
          </td>

          <td>
            <h3
              style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
            >
              Address
            </h3>
            <div>
              Country: {{data.country}}<br />
              Locality: {{data.locality}}<br />
              Address: {{data.address}}
            </div>
          </td>
        </tr>
      </table>
    </td>
  </tr>
`

const PAYMENT_METHOD = html`
  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <h3
        style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
      >
        Refund payment method
      </h3>
      Method: {{#eq data.paymentMethod 'card'}} Credit or debit card used when
      placing the order {{/eq}} {{#eq data.paymentMethod 'bank'}} Bank
      transfer<br />
      IBAN: {{data.iban}} {{/eq}} {{#eq data.paymentMethod 'giftCard'}} Gift
      Card {{/eq}} {{#eq data.paymentMethod 'sameAsPurchase'}} Same as purchase
      {{/eq}}
    </td>
  </tr>
`

const STATUS_UPDATE = html`
  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <h3
        style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
      >
        Return request status
      </h3>
      The current status for your return request is now: <br />
      <b>
        {{#eq data.status 'new'}} New {{/eq}} {{#eq data.status 'processing'}}
        Processing {{/eq}} {{#eq data.status 'pickedUpFromClient'}} Picked up
        from client {{/eq}} {{#eq data.status 'pendingVerification'}} Pending
        verification {{/eq}} {{#eq data.status 'packageVerified'}} Package
        verified {{/eq}} {{#eq data.status 'amountRefunded'}} Amount refunded
        {{/eq}} {{#eq data.status 'denied'}} Denied {{/eq}} {{#eq data.status
        'cancelled'}} Cancelled {{/eq}}
      </b>
    </td>
  </tr>
`

const REFUNDED_MESSAGE = html`
  {{#eq data.status 'refunded'}}
  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <div>
        <h3
          style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
        >
          Refund details
        </h3>
        Refunded value {{#eq data.paymentMethod 'giftCard'}} in the form of a
        gift card worth {{formatCurrency data.refundedAmount}}$
        <br /><br />
        {{/eq}} {{#eq data.paymentMethod 'card'}} by bank card in the amount of
        {{formatCurrency data.refundedAmount}}$<br /><br />
        {{/eq}} {{#eq data.paymentMethod 'bank'}} by bank transfer in the amount
        of {{formatCurrency data.refundedAmount}}$<br />
        <b>IBAN: {{data.iban}}</b>
        {{/eq}}
      </div>
    </td>
  </tr>
  {{/eq}}
`

const DENIED_MESSSAGE = html`
  {{#eq data.status 'denied'}}

  <tr style="box-sizing: border-box !important;">
    <td
      class="ph4-ns"
      style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      align="left"
    >
      <div>
        <h3
          style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;"
        >
          Final state
        </h3>
        <b>Return request rejected</b>
      </div>
    </td>
  </tr>
  {{/eq}}
`

const STATUS_TIMELINE = html`
{{#compare refundStatusData.length ">" 0}}

<tr style="box-sizing: border-box !important;">
    <td class="ph4-ns"
        style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left; border-top-style: solid; border-top-width: 1px; border-bottom-style: solid; border-bottom-width: 1px; border-color: #eee; border-width: .5rem; width: 100%; padding-top: 2rem; padding-bottom: 2rem !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
        align="left">
        <h3 style="margin: 0; font-size: 20px; line-height: 36px; text-transform: uppercase; letter-spacing: 1.2pt; font-weight: 300; box-sizing: border-box; margin-top: 0 !important;">
            Request status timeline</h3>
        <table style="border-collapse:collapse;font-size:14px;width: 36rem">
            <tr>
                <td>
                    <div>
                        {{#each refundStatusData}}
                        <div>
                            <p class="statusLine">
                                {{#unless @last}}

                                <span class="statusIcon statusIconChecked">
                                    <span style="color: #fff; display: block; width: 1.3rem; height: 1.3rem; text-align: center;"><b>✓</b></span>
                                </span>
                                <span>Status:
                                  <b>
                                    {{#eq status 'new'}} New {{/eq}}
                                    {{#eq status 'processing'}} Processing {{/eq}}
                                    {{#eq status 'pickedUpFromClient'}} Picked up from client {{/eq}}
                                    {{#eq status 'pendingVerification'}} Pending verification {{/eq}}
                                    {{#eq status 'packageVerified'}} Package verified {{/eq}}
                                    {{#eq status 'amountRefunded'}} Amount refunded {{/eq}}
                                    {{#eq status 'denied'}} Denied {{/eq}} {{#eq status
                                      'cancelled'}} Cancelled {{/eq}}
                                  </b>
                                </span>
                                <ul class="statusUl">
                                  {{#each comments}}
                                  {{#if visibleForCustomer}}
                                  <li>Comment: {{comment}}</li>
                                  {{/if}}
                                  {{/each}}
                                </ul>

                                {{else}}

                                <span class="statusIcon statusIconChecked">
                                    <span style="color: #fff; display: block; width: 1.3rem; height: 1.3rem; text-align: center;"><b>✓</b></span>
                                </span>
                                <span>Status:
                                  <b>
                                    {{#eq status 'new'}} New {{/eq}}
                                    {{#eq status 'processing'}} Processing {{/eq}}
                                    {{#eq status 'pickedUpFromClient'}} Picked up from client {{/eq}}
                                    {{#eq status 'pendingVerification'}} Pending verification {{/eq}}
                                    {{#eq status 'packageVerified'}} Package verified {{/eq}}
                                    {{#eq status 'amountRefunded'}} Amount refunded {{/eq}}
                                    {{#eq status 'denied'}} Denied {{/eq}} {{#eq status
                                      'cancelled'}} Cancelled {{/eq}}
                                  </b>
                                </span>
                                <ul class="statusUl statusUlLast">
                                  {{#each comments}}
                                  {{#if visibleForCustomer}}
                                  <li>Comment: {{comment}}</li>
                                  {{/if}}
                                  {{/each}}
                                </ul>

                                {{#compare status '!=' 'amountRefunded'}}
                                {{#compare status '!=' 'denied'}}
                                {{#compare status '!=' 'cancelled'}}
                                <span class="statusIcon">
                                    <span style="color: #fff; display: block; width: 1.3rem; height: 1.3rem; text-align: center;"><b>✓</b></span>
                                </span>
                                <span>Status:
                                  <b>
                                    {{#eq status 'new'}} Processing {{/eq}}
                                    {{#eq status 'processing'}} Picked up from client {{/eq}}
                                    {{#eq status 'pickedUpFromClient'}} Pending verification {{/eq}}
                                    {{#eq status 'pendingVerification'}} Package verified {{/eq}}
                                    {{#eq status 'packageVerified'}} Amount refunded {{/eq}}
                                  </b>
                                </span>
                                {{/compare}}
                                {{/compare}}
                                {{/compare}}
                                {{/unless}}
                            </p>
                        </div>
                        {{/each}}
                    </div>
                </td>
            </tr>
        </table>

    </td>
</tr>
{{/compare}}
`

const createTemplate = (headAndStyle: string, ...bodyBlocks: string[]) => {
  return `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:o="urn:schemas-microsoft-com:office:office"
      xmlns:v="urn:schemas-microsoft-com:vml"
      style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; box-sizing: border-box; width: 100%; height: 100%; margin: 0; padding: 0; background: #f1f1f1 !important;"
    >
      ${headAndStyle}
      <body
        style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; box-sizing: border-box; width: 100%; height: 100%; margin: 0; padding: 0; background: #f1f1f1 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
      >
        <table
          width="100%"
          border="0"
          cellpadding="0"
          cellspacing="0"
          style="box-sizing: border-box; margin: 0; padding: 0; background: #f1f1f1; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; width: 100%; height: 100%; line-height: 100% !important;"
        >
          <tr style="box-sizing: border-box !important;">
            <td
              align="left"
              valign="top"
              style="font-size: 14px; line-height: 20px; box-sizing: border-box; border-collapse: collapse; text-align: left !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
            >
              <table
                class="mv4-ns"
                width="100%"
                align="center"
                border="0"
                cellpadding="0"
                cellspacing="0"
                style="box-sizing: border-box; max-width: 40rem; width: 100%; background-color: #fff; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
                bgcolor="#fff"
              >
                ${bodyBlocks.join('\n')}
              </table>
            </td>
          </tr>
        </table>
      </body>
    </html>
  `
}

export const OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE_MESSAGE = createTemplate(
  HEAD_AND_STYLE,
  HEADER,
  PRODUCTS,
  CONTACT_DETAILS,
  PAYMENT_METHOD
)

export const OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE_MESSAGE = createTemplate(
  HEAD_AND_STYLE,
  HEADER,
  STATUS_UPDATE,
  REFUNDED_MESSAGE,
  DENIED_MESSSAGE,
  STATUS_TIMELINE
)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/node/utils/templates/index.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { Template } from '../../typings/mailClient'
import {
  OMS_RETURN_REQUEST_CONFIRMATION,
  OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME,
  OMS_RETURN_REQUEST_STATUS_UPDATE,
  OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME,
} from '../constants'
import {
  OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE_MESSAGE,
  OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE_MESSAGE,
} from './templateMessages'

export const OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE = (
  locale = 'en-GB'
): Template => ({
  Name: OMS_RETURN_REQUEST_CONFIRMATION(locale),
  FriendlyName: OMS_RETURN_REQUEST_CONFIRMATION_FRIENDLY_NAME(locale),
  Description: '[OMS] Return requests confirmation message',
  IsDefaultTemplate: false,
  AccountId: null,
  AccountName: null,
  ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{data.email}}',
      CC: null,
      BCC: '{{#compare data.status "==" \'new\'}}{{/compare}}',
      Subject: 'New Return request {{data.DocumentId}}',
      Message: OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE_MESSAGE,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      ProviderId: null,
      ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
})

export const OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE = (
  locale = 'en-GB'
): Template => ({
  Name: OMS_RETURN_REQUEST_STATUS_UPDATE(locale),
  FriendlyName: OMS_RETURN_REQUEST_STATUS_UPDATE_FRIENDLY_NAME(locale),
  Description: '[OMS] Return requests status update message',
  IsDefaultTemplate: false,
  AccountId: null,
  AccountName: null,
  ApplicationId: null,
  IsPersisted: true,
  IsRemoved: false,
  Type: '',
  Templates: {
    email: {
      To: '{{data.email}}',
      CC: null,
      BCC: '{{#compare data.status "==" \'new\'}}{{/compare}}',
      Subject: 'Return request status update {{data.DocumentId}}',
      Message: OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE_MESSAGE,
      Type: 'E',
      ProviderId: '00000000-0000-0000-0000-000000000000',
      ProviderName: null,
      IsActive: true,
      withError: false,
    },
    sms: {
      Type: 'S',
      ProviderId: null,
      ProviderName: null,
      IsActive: false,
      withError: false,
      Parameters: [],
    },
  },
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of return-app/react/typings/vtex.render-runtime.d.ts from the return-app VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module 'vtex.render-runtime' {
  import type { ComponentType, ReactElement, ReactType } from 'react'

  export interface NavigationOptions {
    page: string
    params?: unknown
  }

  export interface RenderContextProps {
    runtime: {
      navigate: (options: NavigationOptions) => void
    }
  }

  interface ExtensionPointProps {
    id: string

    [key: string]: any
  }

  interface ChildBlock<BlockProps> {
    props: BlockProps
  }

  export const ExtensionPoint: ComponentType<ExtensionPointProps>
  export const useChildBlock: <BlockProps>({
    id,
  }: {
    id: string
  }) => ChildBlock<BlockProps> | null
  export const Helmet: ReactElement
  export const Link: ReactType
  export const NoSSR: ReactElement
  export const RenderContextConsumer: unknown
  export const TreePathContextConsumer: unknown
  export const useTreePath: unknown
  export const useRuntime: unknown
  export const canUseDOM: boolean
  export const withRuntimeContext: <
    TOriginalProps extends Record<string, unknown>
  >(
    Component: ComponentType<TOriginalProps & RenderContextProps>
  ) => ComponentType<TOriginalProps>

  export const withSession: <TOriginalProps extends Record<string, unknown>>(
    Component: ComponentType<TOriginalProps & RenderContextProps>
  ) => ComponentType<TOriginalProps>

  interface KeyValue {
    value: string
  }

  interface Session {
    id: string
    namespaces: {
      store: {
        channel: KeyValue
      }
      profile: {
        isAuthenticated: KeyValue
        email: string
      }
    }
  }

  interface SessionUnauthorized {
    type: 'Unauthorized'
    message: string
  }

  interface SessionForbidden {
    type: 'Forbidden'
    message: string
  }

  type SessionResponse = Session | SessionUnauthorized | SessionForbidden

  export interface RenderSession {
    sessionPromise: Promise<SessionPromise>
  }

  export interface SessionPromise {
    response: Session | SessionUnauthorized | SessionForbidden
  }

  export function useRuntime()
}

```