[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 cy-runner/cypress-shared/support/quick-order/testcase.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
import selectors from '../common/selectors.js'
import { fillQuoteInformation } from '../b2b/quotes.js'
import { BUTTON_LABEL, TOAST_MSG } from '../validation_text.js'
import { GRAPHL_OPERATIONS } from '../graphql_operations.js'
import { validateToastMsg, validateToolTipMsg } from '../b2b/utils.js'
import { updateRetry } from '../common/support.js'

export const POPUP_MSG = "You can't have more than 50 items"
export const TOOLTIP_MSG = {
  skuNotFound: /SKU Not Found/i,
  maxQuantity: /Max quantity is 50(.*)/i,
}

function ProceedToCheckOut() {
  cy.qe('Clicking on proceed to checkout')
  cy.get(selectors.ProceedToCheckOut).should('be.visible').click()
}

function fillSkuAndQuantity(textArea, validate, skuQuantity) {
  cy.qe(
    'Fill the skuquantity ,check the validate text visibility and click on it'
  )
  cy.get(textArea).clear().type(skuQuantity, { force: true })
  cy.get(validate).should('be.visible').click()
}

function checkBackButtonIsVisible() {
  cy.qe('Checking the back button visisbility in the page')
  cy.closeMenuIfOpened()
  const [buttonsBlock, button] = [
    'div[class*=buttonsBlock]',
    'div[class*=vtex-button]',
  ]

  cy.get('body').then(($body) => {
    if ($body.find(buttonsBlock).length) {
      cy.get(button).contains(BUTTON_LABEL.back).click()
    }
  })
}

export function quickOrderBySkuAndQuantityTestCase2(role, b2b = true) {
  it(
    `Verify ${role} is able remove invalid skus in quick order - [Sku's Code],[Quantity]`,
    updateRetry(2),
    () => {
      const { textArea, validate } = selectors.QuickOrderPage().skus

      cy.gotoQuickOrder(b2b)
      checkBackButtonIsVisible()
      fillSkuAndQuantity(textArea, validate, '880270a,2{enter}1,2{enter}')
      checkBackButtonIsVisible()
    }
  )
}

export function quickOrderBySkuAndQuantityTestCase1(
  role,
  quoteEnv = false,
  totalPrice = '$180.00'
) {
  const title = `Verify ${role} is able to ${
    quoteEnv
      ? 'create quote by quick order'
      : 'add product to cart & perform checkout'
  } - [Sku's Code],[Quantity]`

  it(title, updateRetry(2), () => {
    const { textArea, validate, invalid, content, addtoCart } =
      selectors.QuickOrderPage().skus

    cy.gotoQuickOrder(quoteEnv)
    checkBackButtonIsVisible()
    fillSkuAndQuantity(textArea, validate, '1,2{enter}')
    cy.get(invalid).should('be.visible')
    cy.get(content).clear().type('880270a,2{enter}').focus()
    cy.get(invalid).click()
    cy.qe(`Click on Add to cart`)
    cy.waitForGraphql(GRAPHL_OPERATIONS.addToCart, addtoCart)
    validateToastMsg(TOAST_MSG.addedToTheCart)
    cy.qe(`Open the cart and click on the first item`)
    cy.get(selectors.OpenCart).first().should('be.visible').click()
    cy.qe(`The minicart should contain the product name Cauliflower`)
    cy.get(selectors.MiniCartProductName).should('contain', 'Cauliflower')
    cy.qe(`Verifying the total price`)
    cy.get(selectors.TotalPrice).should('have.text', totalPrice)
    quoteEnv ? fillQuoteInformation({ quoteEnv }) : ProceedToCheckOut()
  })
}

export function quickOrderBySkuAnd51QuantityTestCase(role, b2b = true) {
  it(
    `Verify ${role} is able to add 50 products to cart with 51 quantity by quick order - [Sku's Code],[Quantity]`,
    updateRetry(2),
    () => {
      const { textArea, validate, addtoCart } = selectors.QuickOrderPage().skus

      cy.gotoQuickOrder(b2b)
      checkBackButtonIsVisible()
      fillSkuAndQuantity(textArea, validate, '880270a,51{enter}')
      cy.qe('The addtoCart button should not exist')
      cy.get(addtoCart).should('not.exist')
      validateToolTipMsg(TOOLTIP_MSG.maxQuantity)
    }
  )
}

export function quickOrderBySkuAndQuantityWithValidAndInValidSkuTestCase(
  role,
  b2b = true
) {
  it(
    `Verify ${role} is not able to use one invalid & valid skus  - [Sku's Code],[Quantity]`,
    updateRetry(2),
    () => {
      const { textArea, validate, addtoCart } = selectors.QuickOrderPage().skus

      cy.gotoQuickOrder(b2b)
      checkBackButtonIsVisible()
      fillSkuAndQuantity(textArea, validate, '880270a,51{enter}1,2{enter}')
      cy.qe('The addtoCart button should not exist')
      cy.get(addtoCart).should('not.exist')
      validateToolTipMsg(TOOLTIP_MSG.maxQuantity, 0)
      validateToolTipMsg(TOOLTIP_MSG.skuNotFound, 1)
    }
  )
}

function searchOneByOneProduct(search, { product, quantity }, number) {
  cy.qe(`Search for the product ${product}`)
  cy.get(search).should('be.visible').should('be.enabled').clear()
  cy.get(search).should('be.visible').type(product)
  // Product will be shown in results only after below selector gets visibled
  cy.get('button[class*=customOption]').should('be.visible')
  cy.get(search).type('{downarrow}{enter}')
  number > 1 &&
    cy
      .get(quantity, { timeout: 10000 })
      .should('be.visible')
      .clear()
      .type(`${number}`)
}

export function quickOrderByOneByOneTestCase(
  role,
  product,
  quoteEnv,
  totalPrice = '$540.00'
) {
  it(
    `Verify ${role} is able to ${
      quoteEnv ? 'create quote by' : 'use'
    } quick order - One by One`,
    updateRetry(2),
    () => {
      cy.gotoQuickOrder(quoteEnv)
      const { search, quantity, add } = selectors.QuickOrderPage().oneByOne

      searchOneByOneProduct(search, { product, quantity }, 1)
      cy.get(add).should('be.visible').click()
      cy.get(selectors.QuickOrderPage().popupMsgSelector).should('be.visible')
      cy.get(selectors.ToastMsgInB2B)
        .should('be.visible')
        .contains('added to the cart')
      cy.get(selectors.OpenCart).first().click()
      cy.get(selectors.MiniCartProductName).should('contain', product)
      cy.get(selectors.TotalPrice).should('have.text', totalPrice)
      quoteEnv && fillQuoteInformation({ quoteEnv })
    }
  )
}

export function quickOrderByOneByOneNegativeTestCase(
  role,
  product,
  quoteEnv,
  totalPrice = '$27,000.00'
) {
  it(
    `Verify ${role} is able to ${
      quoteEnv ? 'create quote by' : 'use'
    } quick order with 51 products - One by One`,
    updateRetry(2),
    () => {
      cy.gotoQuickOrder(quoteEnv)
      const { search, quantity, add } = selectors.QuickOrderPage().oneByOne

      searchOneByOneProduct(search, { product, quantity }, 51)
      cy.get(add).should('be.visible').click()
      cy.qe(`Verify the ${POPUP_MSG} in the QuickorderPage`)
      cy.get(selectors.QuickOrderPage().popupMsgSelector).contains(POPUP_MSG)
      cy.qe('Opening the cart and selecting the first item')
      cy.get(selectors.OpenCart).first().click()
      cy.qe(`Verifying if the mininCartproductname contains ${product}`)
      cy.get(selectors.MiniCartProductName)
        .should('be.visible')
        .should('contain', product)
      cy.qe(`Verifying the totalprice - ${totalPrice}`)
      cy.get(selectors.TotalPrice).should('have.text', totalPrice)
      cy.qe('The minicartQuantity should have the value of 50')
      cy.get(selectors.MiniCartQuantityForMaxOrder).should('have.value', '50')
      // Use the product which is already added in cart
      quoteEnv && fillQuoteInformation({ quoteEnv })
    }
  )
}

function quickOrderCategory(quoteEnv, number, totalPrice) {
  cy.gotoQuickOrder(quoteEnv)
  const { product, addtoCart, quantity } = selectors.QuickOrderPage().categories

  cy.qe('The addtoCart should be visible and it should contain the product')
  cy.get(addtoCart).should('be.visible')
  cy.qe(`Click the product - ${product}`)
  cy.contains(product).should('be.visible').click()
  cy.get(quantity, { timeout: 15000 })
    .first()
    .scrollIntoView()
    .clear({ timeout: 8000 })
    .type(number, { force: true })
  cy.qe('The addtoCart should be visible and we should click on the product')
  cy.get(addtoCart).should('be.visible').click()
  cy.get(selectors.ToastMsgInB2B, { timeout: 10000 })
    .should('be.visible')
    .contains(number > 50 ? POPUP_MSG : TOAST_MSG.addedToTheCart)
  cy.qe(`The openCart is visible and clickable`)
  cy.get(selectors.OpenCart).first().should('be.visible').click()
  cy.qe('The MiniCartProductname should contain Golf Shoes')
  cy.get(selectors.MiniCartProductName).should('contain', 'Golf Shoes')
  cy.qe(`Verifying the totalPrice - ${totalPrice}`)
  cy.get(selectors.TotalPrice).should('have.text', totalPrice)
  if (number > 50) {
    cy.get(selectors.MiniCartQuantityForMaxOrder).should('have.value', '50')
  }

  // Use the product which is already added in cart
  quoteEnv && fillQuoteInformation({ quoteEnv })
}

export function quickOrderByCategory(role, quoteEnv, totalPrice = '$94.00') {
  it(
    `Verify ${role} is able ${
      quoteEnv ? `to create quote by` : `use`
    } quick order - Categories`,
    updateRetry(2),
    () => {
      quickOrderCategory(quoteEnv, 1, totalPrice)
    }
  )
}

export function quickOrderByCategoryNegativeTestCase(
  role,
  quoteEnv,
  totalPrice = '$4,700.00'
) {
  it(
    `Verify ${role} is able to create quote by quick order with 51 products - Categories`,
    updateRetry(2),
    () => {
      quickOrderCategory(quoteEnv, 51, totalPrice)
    }
  )
}

function validateForm(quoteEnv, vtex, productCount) {
  cy.qe('Adding intercept for validateForm')
  cy.intercept('POST', `${vtex.baseUrl}/**`).as('validateForm')
  // eslint-disable-next-line cypress/no-unnecessary-waiting
  cy.wait(5000)
  cy.qe('Verifying if AddToCart button is visible and clicking on it')
  cy.contains(BUTTON_LABEL.AddToCart).should('be.visible').click()
  cy.wait('@validateForm')
  cy.qe('Validate the Quantity in QuantityBadgeCart')
  cy.get(selectors.QuantityBadgeInCart, { timeout: 15000 }).should(
    'have.text',
    productCount
  )
  cy.qe('Verifying if openCart is visible and then click on it')
  cy.get(selectors.OpenCart).first().should('be.visible').click()
  quoteEnv ? fillQuoteInformation({ quoteEnv }) : ProceedToCheckOut()
}

function uploadXLS(filePath, b2b) {
  const { deleteFile } = selectors.QuickOrderPage().uploadXLS

  cy.qe(`The profile should be visible on the QuickOrder home page`)
  cy.gotoQuickOrder(b2b)

  // If deleteFile selector exist then, it means we are retrying this testcase
  // close and upload CSV
  cy.get('body').then(($body) => {
    if ($body.find(deleteFile).length) {
      cy.get(deleteFile, { timeout: 10000 })
        .should('be.visible')
        .click({ multiple: true })
    }
  })
  cy.qe(`Check the backbutton visibility in the page`)
  checkBackButtonIsVisible()
  cy.qe(`Attaching the Xls file `)
  cy.get(selectors.QuickOrderPage().uploadXLS.file, {
    timeout: 10000,
  }).attachFile(filePath)
  cy.qe(`The validate button should be enabled, and visible;  then click on it`)
  cy.get(selectors.QuickOrderPage().uploadXLS.validate)
    .should('be.visible')
    .should('be.enabled')
    .click()
}

export function quickOrderByXLS(quoteEnv = false) {
  it(`Create quick order by uploading excel`, updateRetry(3), () => {
    cy.getVtexItems().then((vtex) => {
      cy.qe(`Pass the pathname to upload an XLS file`)
      const filePath = 'model-quickorder.xls'

      uploadXLS(filePath, quoteEnv)
      validateForm(quoteEnv, vtex, 2)
    })
  })
}

function validateNegativeTestCase() {
  cy.contains(BUTTON_LABEL.AddToCart).should('not.exist')
}

export function quickOrderByXLSNegativeTestCase2(quoteEnv = false) {
  it(`Create quick order with above max quantity`, updateRetry(3), () => {
    const filePath = 'quickorder_with_max_quantity.xls'

    cy.qe('Uploading the XLS file with one valid and one invalid sku')
    uploadXLS(filePath, quoteEnv)
    cy.qe('The addToCart button should not exist')
    validateNegativeTestCase()
    cy.qe(`Validate the ${TOOLTIP_MSG.maxQuantity}`)
    validateToolTipMsg(TOOLTIP_MSG.maxQuantity)
  })
}

export function quickOrderByXLSNegativeTestCase(quoteEnv) {
  it(
    `Create quick order by uploading xls with one valid and one invalid sku line item`,
    updateRetry(3),
    () => {
      const filePath = 'model-quickorder1.xls'

      cy.qe('Uploading the XLS file with one valid and one invalid sku')
      uploadXLS(filePath, quoteEnv)
      cy.qe(`Validate the ${TOOLTIP_MSG.maxQuantity}`)
      validateToolTipMsg(TOOLTIP_MSG.maxQuantity)
      cy.qe(`Validate the ${TOOLTIP_MSG.skuNotFound}`)
      validateToolTipMsg(TOOLTIP_MSG.skuNotFound, 1)
      cy.get('svg[class*=vtex__icon-delete]:nth-child(1)').last().click()
      cy.qe('Validate addToCart should not exist')
      validateNegativeTestCase()
      cy.qe(`Validate the ${TOOLTIP_MSG.maxQuantity}`)
      validateToolTipMsg(TOOLTIP_MSG.maxQuantity)
    }
  )
}

export function verifyExcelFile(fileName, products) {
  it('verify the data and extension', updateRetry(3), () => {
    cy.qe(`The XLS data should be equal and it should be greater than one`)
    cy.task('readXlsx', {
      file: fileName,
      sheet: 'SheetJS',
    }).then((rows) => {
      expect(rows.length).to.be.greaterThan(1)
      expect(JSON.stringify(rows)).to.be.equal(JSON.stringify(products))
    })
  })
}

```


[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 cy-runner/cypress-shared/support/quick-order/product.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
export const products = [
  {
    SKU: 'AB120',
    Quantity: 2,
  },
  {
    SKU: 'AB121',
    Quantity: 3,
  },
  {
    SKU: 'AB122',
    Quantity: 5,
  },
  {
    SKU: 'AB123',
    Quantity: 10,
  },
  {
    SKU: 'AB124',
    Quantity: 1,
  },
  {
    SKU: 'AB125',
    Quantity: 20,
  },
]

```


[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 cy-runner/cypress-shared/support/location-availability/shipping-policy.json from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "ship1": {
    "shippingPolicy": {
      "numberOfItemsPerShipment": 1,
      "id": "sha1920ede3r",
      "name": "Test Shipping",
      "shippingMethod": "Normal (1234)",
      "carrierSchedule": [
        {
          "dayOfWeek": 0,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 1,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 2,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 3,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 4,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 5,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 6,
          "timeLimit": "23:59:59"
        }
      ],
      "pickupPointsSettings": {
        "pickupPointIds": ["1", "2", "3"],
        "pickupPointTags": [],
        "sellers": []
      },
      "shippingHoursSettings": {
        "shippingHours": [
          {
            "dayOfWeek": 0,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 1,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 2,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 3,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 4,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 5,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 6,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          }
        ],
        "acceptOrdersOutsideShippingHours": true
      }
    }
  },
  "ship2": {
    "shippingPolicy": {
      "numberOfItemsPerShipment": 1,
      "id": "LAS",
      "name": "Location Availability Shipping",
      "shippingMethod": "Normal (1234) (P1)",
      "carrierSchedule": [
        {
          "dayOfWeek": 0,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 1,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 2,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 3,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 4,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 5,
          "timeLimit": "23:59:59"
        },
        {
          "dayOfWeek": 6,
          "timeLimit": "23:59:59"
        }
      ],
      "pickupPointsSettings": {
        "pickupPointIds": ["4"],
        "pickupPointTags": [],
        "sellers": []
      },
      "shippingHoursSettings": {
        "shippingHours": [
          {
            "dayOfWeek": 0,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 1,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 2,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 3,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 4,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 5,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          },
          {
            "dayOfWeek": 6,
            "ranges": [
              {
                "openingTime": "00:00:00",
                "closingTime": "23:59:59"
              }
            ]
          }
        ],
        "acceptOrdersOutsideShippingHours": true
      }
    }
  }
}

```


[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 cy-runner/cypress-shared/support/location-availability/support.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
import selectors from '../common/selectors'
import { updateRetry } from '../common/support'
import { getPickupPoints, deletePickupPoint } from './pickup-points.api'

export function verifyUpdatedAddress(postalCode, address, city, state) {
  it('Verify on click to postal code it opens the location popup', () => {
    cy.qe('In specification page clicking on address container')
    cy.get(selectors.AvailabilityHeader).click()
    cy.qe(
      'Once address container clicked, it will show an popup of layout to add shipping address'
    )
    cy.get(selectors.AddressModelLayout).should('be.visible')
    cy.get(selectors.countryDropdown).select('USA')
    cy.get(selectors.addressInputContainer).eq(0).clear().type(postalCode)
    if (city) {
      cy.get(selectors.Address)
        .contains('City')
        .parent()
        .within(() => {
          cy.get(selectors.InputText)
            .should('not.have.value', '')
            .clear()
            .type(city)
        })
    }

    if (address) {
      cy.get(selectors.Address)
        .contains('Address Line 1')
        .parent()
        .within(() => {
          cy.get(selectors.InputText).clear().type(address, { delay: 50 })
        })
    }

    if (state) {
      cy.get(selectors.ProvinceField).should('exist').select(state)
    }

    cy.waitForGraphql('setRegionId', selectors.SaveButtonInChangeLocationPopUp)
    cy.once('uncaught:exception', () => false)
  })
  it('Verify updated address is shown in the screen', updateRetry(2), () => {
    cy.get(selectors.AvailabilityHeader).should('have.text', postalCode)
    cy.get(selectors.AddtoCart)
      .contains(/Add to Cart/i)
      .click({ force: true })
    cy.get(selectors.ProceedtoCheckout).click()
    cy.get(selectors.orderButton).should('be.visible').click()
  })
}

export function addPickUpPoint(pickPointName, pickUpId) {
  cy.qe('Clicking Add Pickup Point button in admin')
  cy.contains(/Add Pickup Point/i).click()
  cy.qe('Type a new pickuppoint name')
  cy.get(selectors.PickUpPointName).clear().type(pickPointName)
  cy.qe('Type a new pickuppoint id')
  cy.get(selectors.PickUpPointId).should('be.visible').type(pickUpId)
  cy.qe('Select a USA country')
  cy.get('select')
    .select('United States of America')
    .should('have.value', 'USA')
  /* eslint-disable cypress/no-unnecessary-waiting */
  cy.wait(1000)
  /* eslint-disable cypress/no-unnecessary-waiting */
  cy.qe('Type a street address')
  cy.get(selectors.PickUpAddress)
    .type('1481 Maple View Dr,Promona,CA,USA', { delay: 50 })
    .wait(500)
    .type('{downarrow}{enter}')
  cy.get(selectors.CheckBox).click()
  cy.qe('Adding business hours')
  cy.get(selectors.WorkStartTime).eq(1).type('10:00')
  cy.get(selectors.WorkEndTime).eq(1).type('19:00')
  cy.get(selectors.SaveChanges).click()
  cy.qe("Once save button clicked it show an popup as 'Changes saved'")
  cy.get(selectors.ChangesSaved).should('be.visible').contains('Changes saved')
  cy.contains('Back to pickup points').click()
}

export function deleteAllPickupPoints() {
  const FILTER_PICKUP_POINT_KEY = 'Location availability'

  it(
    `Filter and delete pickup point which starts with "${FILTER_PICKUP_POINT_KEY}"`,
    updateRetry(5),
    () => {
      cy.getVtexItems().then((vtex) => {
        cy.getAPI(getPickupPoints(vtex.baseUrl)).then((response) => {
          // Pickup points created in E2E tests should start with text "pickup example"
          // If we create other pickup points then it will not be deleted in wipe
          const filterPickUpPoints = response.body.filter((b) =>
            b.name.includes(FILTER_PICKUP_POINT_KEY)
          )

          if (filterPickUpPoints.length > 0) {
            for (const element of filterPickUpPoints) {
              cy.callRestAPIAndAddLogs({
                method: 'DELETE',
                url: deletePickupPoint(vtex.baseUrl, element.id),
              }).then((deleteResponse) => {
                expect(deleteResponse.status).to.equal(204)
              })
            }
          }
        })
      })
    }
  )
}

```


[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 cy-runner/cypress-shared/support/location-availability/pickup-points.api.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
export default {
  getPickupPoints: (baseUrl) => {
    return `${baseUrl}/api/logistics/pvt/configuration/pickuppoints`
  },
  deletePickupPoint: (baseUrl, pickupPointid) => {
    return `${baseUrl}/api/logistics/pvt/configuration/pickuppoints/${pickupPointid}`
  },
}

```


[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 cy-runner/cypress-shared/support/location-availability/common.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
import selectors from '../common/selectors'

export function verifyHomePage(city, postalCode, distance = false) {
  cy.scrollTo(0, 500)
  cy.getVtexItems().then((vtex) => {
    cy.intercept('POST', `${vtex.baseUrl}/**`, (req) => {
      if (req.body.operationName === 'updateOrderFormShipping') {
        req.continue()
      }
    }).as('updateOrderFormShipping')
    // eslint-disable-next-line cypress/no-unnecessary-waiting
    cy.wait(10000)
    cy.get(selectors.addressContainer).should('be.visible')
    if (distance) {
      cy.get(selectors.Distance).should('not.exist')
      cy.get(selectors.shippingInfo).contains('Get it in')
    } else {
      cy.get(selectors.AddressCity).contains(city)
      cy.get(selectors.AddressZip).contains(postalCode)
      cy.get(selectors.Distance).contains('Distance:')
      cy.wait('@updateOrderFormShipping', { timeout: 20000 })
    }
  })
}

```


[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 cy-runner/cypress-shared/support/location-availability/product.js from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```javascript
export default {
  orange: {
    name: 'Navel Oranges Grown Large Fresh Fruit',
    link: 'a[href*="/navel-oranges-grown-large-fresh-fruit/p"]',
  },
  coconut: {
    name: 'Fresh Coconuts',
    link: 'a[href="/fresh-coconuts/p"]',
  },
}

```


[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 cy-runner/cypress-shared/fixtures/fedex-shipping-fixtures/shippingOptimizePayload.json from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "data": {
    "items": [
      {
        "id": "880350",
        "quantity": 1,
        "groupId": null,
        "unitPrice": 94.0,
        "modal": "",
        "unitDimension": {
          "weight": 10,
          "height": 10,
          "width": 10,
          "length": 10
        }
      },
      {
        "id": "80008075",
        "quantity": 1,
        "groupId": null,
        "unitPrice": 139.0,
        "modal": "",
        "unitDimension": {
          "weight": 12,
          "height": 16,
          "width": 16,
          "length": 10
        }
      }
    ],
    "origin": {
      "zipCode": "33020",
      "country": "USA",
      "state": "FL",
      "city": "Hollywood",
      "coordinates": null,
      "residential": false
    },
    "destination": {
      "zipCode": "00010002",
      "country": "USA",
      "state": null,
      "city": null,
      "coordinates": null,
      "residential": false
    },
    "shippingDateUTC": "2022-05-31T01:02:45.128577+00:00",
    "currency": null
  }
}

```


[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 cy-runner/cypress-shared/fixtures/fedex-shipping-fixtures/shippingRatePayload.json from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "data": {
    "items": [
      {
        "id": "880340",
        "quantity": 1,
        "groupId": null,
        "unitPrice": 500.0,
        "modal": "",
        "unitDimension": {
          "weight": 10.0,
          "height": 10,
          "width": 10,
          "length": 10
        }
      }
    ],
    "origin": {
      "zipCode": "33020",
      "country": "USA",
      "state": "FL",
      "city": "Hollywood",
      "coordinates": null,
      "residential": false
    },
    "destination": {
      "zipCode": "00010002",
      "country": "USA",
      "state": null,
      "city": null,
      "coordinates": null,
      "residential": false
    },
    "shippingDateUTC": "2022-05-31T01:02:45.128577+00:00",
    "currency": null
  }
}

```


[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 cy-runner/cypress-shared/fixtures/fedex-shipping-fixtures/multiProductPayload.json from the cy-runner VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "data": {
    "items": [
      {
        "id": "880340",
        "quantity": 1,
        "groupId": null,
        "unitPrice": 94.0,
        "modal": "",
        "unitDimension": {
          "weight": 10,
          "height": 10,
          "width": 10,
          "length": 10
        }
      },
      {
        "id": "880330",
        "quantity": 1,
        "groupId": null,
        "unitPrice": 94.0,
        "modal": "",
        "unitDimension": {
          "weight": 10,
          "height": 10,
          "width": 10,
          "length": 10
        }
      }
    ],
    "origin": {
      "zipCode": "33020",
      "country": "USA",
      "state": "FL",
      "city": "Hollywood",
      "coordinates": null,
      "residential": false
    },
    "destination": {
      "zipCode": "00010002",
      "country": "USA",
      "state": null,
      "city": null,
      "coordinates": null,
      "residential": false
    },
    "shippingDateUTC": "2022-05-31T01:02:45.128577+00:00",
    "currency": null
  }
}

```


[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 suiteshare/docs/README.md from the suiteshare VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Suiteshare
[Suiteshare](https://apps.vtex.com/vtex-suite-share/p) is a sales and service platform for WhatsApp, and with this app, you can:
- Create solutions to attract and capture more customers.
- Organize your support team’s demands.
- Analyze the results to find out where your customers are.
- Optimize your campaigns.

This app integrates your store with the Suiteshare platform, adding the WhatsApp widget to all pages. The Suiteshare widget provides easy access for communication through Whatsapp and includes several configurations, triggers, and personalized messages customized on their platform.

>Suiteshare app is only compatible with IO Stores.

## Step by Step
1. Go to [VTEX App Store](https://apps.vtex.com/vtex-suite-share/p).
2. Click on the button _GET APP_.
3. Type your account and click on _CONFIRM_.
4. Click on _PLACE ORDER_ to finish your purchase and agree with Suiteshare terms and conditions.

>The app purchase is free. However, it requires an external subscription to the service provided by Suiteshare, which you can do on [Suiteshare page](https://suiteshare.com).

5. Go to the app settings and type your _Suiteshare Id_. You can find your Suiteshare Id on the main Suiteshare dashboard. It is the last code on the installation instructions, and you type only the characters in, for example:

      `ba776dfa9e2fe90641696945dfea429113110686`

![SuiteshareId](https://user-images.githubusercontent.com/1629129/119191255-c0d2ed00-ba54-11eb-94a1-13027c6c1c51.png)

After completing the app installation and setup, you are all set! Additional settings can be found in your [Suiteshare account](https://suiteshare.com/auth/signin).

```


[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 suiteshare/public/metadata/messages/es-AR.json from the suiteshare VTEX app.

[Assistant]
Here's the implementation:

```json
{
    "name": "Suiteshare",
    "headline": "Integre su tienda con la plataforma Suiteshare.",
    "overview": "Esta aplicación se integra con la plataforma Suiteshare, agregando un botón para whatsapp en todas las páginas.\\nEl widget proporciona al cliente un fácil acceso para comunicarse a través de Whatsapp y tiene varias configuraciones, disparadores y mensajes personalizados configurados en Suiteshare.",
    "features": [
      "Hace que el widget de WhatsApp de Suiteshare esté disponible en todas las páginas"
    ],
    "additionalInformation": "Para utilizar esta app, es necesario tener una cuenta de Suiteshare activa y configurar el ID de integración en la configuración de la app.",
    "websiteURL": "https://vtex.com/",
    "support": {
      "url": "https://support.vtex.com/hc/requests"
    }
}
```


[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 suiteshare/public/metadata/messages/pt-BR.json from the suiteshare VTEX app.

[Assistant]
Here's the implementation:

```json
{
    "name": "Suiteshare",
    "headline": "Integre sua loja com a plataforma Suiteshare.",
    "overview": "Este aplicativo realiza a integração com a plataforma Suiteshare, adicionando um botão para o whatsapp em todas as páginas.\\nO widget promove ao cliente um fácil acesso para comunicação via Whatsapp e possui diversas configurações, gatilhos, e mensagens personalizadas configurados pelo Suiteshare.",
    "features": [
      "Disponibiliza o widget de whatsapp da Suiteshare em todas as páginas"
    ],
    "additionalInformation": "Para utilizar esse App é necessário ter uma conta ativa no Suiteshare e configurar o ID de integração nas configurações do App.",
    "websiteURL": "https://vtex.com/",
    "support": {
      "url": "https://support.vtex.com/hc/requests"
    }
}
```


[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 suiteshare/public/metadata/messages/en-US.json from the suiteshare VTEX app.

[Assistant]
Here's the implementation:

```json
{
    "name": "Suiteshare",
    "headline": "Integrate your store with the Suiteshare platform.",
    "overview": "This app integrates your store with the Suiteshare platform, adding the whatsapp widget to all pages.\\nThe widget provides the customer an easy access for communication through Whatsapp and includes several configurations, triggers, and personalized messages and can be customized on Suiteshare.",
    "features": [
      "Adds Suiteshare's whatsapp widget to all store pages"
    ],
    "additionalInformation": "In order to use this app you need to have an active account on Suiteshare and configure your integration ID in the App Settings.",
    "websiteURL": "https://vtex.com/",
    "support": {
      "url": "https://support.vtex.com/hc/requests"
    }
}
```


[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 external-promotion-provider-middleware/docs/README.md from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Promotion Provider Middleware

##  ⚠ Maintenance ⚠
*Starting June 1st 2023, this application will no longer be maintained by VTEX.*

---

This app allows external providers to apply promotions to any SKUs in the shopping cart during an end-user checkout experience.

## Getting Started

You can install it through the command-line interface:

```
vtex install vtex.promotion-provider-middleware@0.x
```

Right after installation, the external endpoint needs to be set. To make this configuration, go to the page `https://{{accountName}}.myvtex.com/admin/apps` , search for `Promotion Provider Middleware`, click on the Settings button, set the External Endpoint and save your configurations.

Upon saving the settings, this application will set the whole environment to comply with its necessities, meaning that the following actions will be done automatically by the app:

* `allowManualPrice` will be set to `true` in the [orderForm Configurations](https://developers.vtex.com/vtex-rest-api/reference/configuration#getorderformconfiguration);
* Create a new object in the `apps` array at [orderForm Configurations](https://developers.vtex.com/vtex-rest-api/reference/configuration#getorderformconfiguration), this allows your promotions data to be stored in the customData object (inside orderForm) for every order.

Upon installing, the public routes will become instantly available to use.

It's worth mentioning that Order Authorization configurations could interfere with key functional aspects of this application. If the account has any Manual Discount rules configured, make sure to verify if the promotions applied by the external provider comply with the range set in Manual Discount rules. The reason for the impact is that this application makes use of [Update cart items](https://developers.vtex.com/vtex-rest-api/reference/cart-update#itemsupdate) to apply discounts.

## Syntax and Supported Routes

### Notification:
`POST /_v/promotion-provider/notification`

This route is responsible for consulting the external provider and applying the discounts afterwards.

All this route needs to be initiated is the cookie `checkout.vtex.com` containing the orderformId in the format `__ofid={{orderFormId}}`. After receiving this cookie in the headers, the middleware will go through the following steps:

* Fetch the orderForm through the orderFormId sent inside the cookie;
* Clean all manual prices currently active in the orderForm;
* Parse the orderForm according to the protocol (more details later on);
* Send the payload to the external provider.

> 🔎 It is worth mentioning that there's a 12.5 seconds timeout attributed to the external provider with 0 retries. Meaning that the external provider has this exact time to deliver a response to our application.
The object sent to the external provider looks like the following:
> Observation: The objects customData, shippingData, clientProfileData, paymentData are exactly the same as seen in the orderForm.
```typescript
interface ExternalPromotionsRequestProtocol {
  items: {
    id: string
    productId: string
    refId?: any
    ean: string
    name: string
    skuName: string
    modalType?: any
    productCategoryIds: string
    productCategories: ProductCategories
    availability: string
    measurementUnit: string
    variations: {
      index: number
      assemblies: any[]
      tax: number
      price: number
      listPrice: number
      manualPrice?: any
      sellingPrice: number
      isGift: boolean
      quantity: number
      attachments: Attachment[]
      attachmentOfferings: AttachmentOffering[]
      offerings: any[]
      priceTags: PriceTag[]
      unitMultiplier: number
      seller: string
      sellerChain: string[]
    }[]
  }[]
  customData: CustomData
  shippingData: ShippingData
  clientProfileData: ClientProfileData
  marketingData?: MarketingData
  paymentData: PaymentData
  totalizers: {
    id: string
    name: string
    value: number
  }[]
}
```

When it comes to the external provider response, we expect the following objects (example at the end of this documentation):

```typescript
interface ExternalPromotionsResponseProtocol {
  items: {
    id: number
    variations: {
      requestIndex: number
      quantity: number
      externalPromotions: {
        matchedParameters: {[key: string]: string}
        identifier: string
        isPercentual: boolean
        value: number
      }[]
    }[]
  }[]
}
```

It's important to notice that in cases where `isPercentual` is `true`, the `value` property should contain numbers between `0` and `1`, for example: `0.35`. If `isPercentual` is `false`, the `value` property should have negative values such as `-100` or `-2500` for a nominal discount of $1,00 and $25,00 respectively.

The `requestIndex` field (present in the external provider response) should match the index from the payload sent while also respecting it's variations. If the payload sent has 3 SKU's, but 1 SKU has 2 variations (due to split index), then your request index could vary from 0 to 4. If this is not respected, the middleware will throw an error and not apply any discount.

> 🚨 Warning: The `items` array (present in the external provider response) should <strong>only</strong> contain the items that will be affected by any promotion. If no items should be impacted, then the `items` array should be sent empty.

Another important observation is that the discount will be applied over the summed `price` of the skuId based on its quantity. Also worth mentioning that percentual discounts will be applied first and nominal discounts afterwards.

> ❗ Reminder: We are talking about the `price` property as found in the orderForm, we are <strong>not</strong> talking about `sellingPrice` nor `listPrice`.

The `matchedParameters` property should have `key` and `value` as strings with a maximum of 50 characters.

After the external provider response, if it all went well, the middleware will use the API [Update cart items](https://developers.vtex.com/vtex-rest-api/reference/cart-update#itemsupdate) to update the prices in the cart respecting the discounts sent.

An important reminder, every promotion configured in the promotions module is equipped with a flag called `Allows accumulate with manual prices`, if this flag is checked, first the external promotion will be applied, and only then the other promotions will apply. If this flag remains unchecked, when the external promotion gets applied, the respective promotion will not be applied.

### Simulation:
`POST /_v/promotion-provider/simulation`

This route is only responsible for consulting the external provider, and <strong>does not</strong> apply discounts.

In order to put this route in use, all you need to do is send a request body containing the `items` array, which for this route is exactly the same as it is in the orderForm.

Besides the `items` array, you could also send `customData`, `shippingData`, `clientProfileData`, `marketingData`, `paymentData`.

> Observation: The only field required in this request is the `id` property inside each object present in the `items` array.

## External Provider Response Example (Response Body):

```json
{
    "items": [
        {
            "id": 33, //required
            "variations": [
                {
                    "requestIndex": 0, //required
                    "quantity": 1, //required
                    "externalPromotions": [
                        {
                            "matchedParameters": {
                                "slaIds": "transportadora genérica"
                            },
                            "identifier": "anyName",
                            "isPercentual": false, //required
                            "value": -200 //required
                        },
                        {
                            "matchedParameters": {
                                "skuId": "33"
                            },
                            "identifier": "10off",
                            "isPercentual": true, //required
                            "value": 0.10 //required
                        }
                    ]
                },
                {
                    "requestIndex": 1, //required
                    "quantity": 1, //required
                    "externalPromotions": [
                        {
                            "isPercentual": true, //required
                            "value": 0.50 //required
                        }
                    ]
                }
            ]
        }
    ]
}
```

```


[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 external-promotion-provider-middleware/node/service.json from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 256,
  "ttl": 120,
  "timeout": 30,
  "minReplicas": 2,
  "maxReplicas": 4,
  "workers": 1,
  "routes": {
    "orderFormNotification": {
      "path": "/_v/promotion-provider/notification",
      "public": true
    },
    "simulation": {
      "path": "/_v/promotion-provider/simulation",
      "public": true
    }
  },
  "events": {
    "onAppInstalled": {
      "sender": "apps",
      "keys": ["setup"],
      "settingsType": "workspace"
    },
    "onSettingsChanged": {
      "sender": "apps",
      "keys": ["settings"]
    }
  }
}

```


[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 external-promotion-provider-middleware/node/package.json from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "dependencies": {
    "@vtex/clients": "^2.13.0",
    "co-body": "^6.0.0",
    "ramda": "^0.25.0",
    "yup": "^0.32.9"
  },
  "devDependencies": {
    "@types/co-body": "^0.0.3",
    "@types/jest": "^24.0.18",
    "@types/node": "^12.0.0",
    "@types/ramda": "types/npm-ramda#dist",
    "@vtex/api": "6.45.12",
    "@vtex/test-tools": "^1.0.0",
    "@vtex/tsconfig": "^0.5.6",
    "typescript": "3.9.7"
  },
  "scripts": {
    "lint": "tsc --noEmit --pretty",
    "test": "vtex-test-tools test --collectCoverage"
  },
  "version": "0.24.6"
}

```


[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 external-promotion-provider-middleware/node/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  ClientsConfig,
  ServiceContext,
  RecorderState,
  EventContext,
} from '@vtex/api'
import { method, LRUCache, Service } from '@vtex/api'

import { Clients } from './clients'
import {
  setupAppConfiguration,
  getOrderFormById,
  calculateExternalBenefits,
  applyManualPrices,
  getAppConfiguration,
} from './middlewares'
import type { CheckoutOrderForm } from './typings/global'
import type { ExternalPromotionsRequestProtocol } from './typings/protocol/request'
import type { ExternalPromotionsResponseProtocol } from './typings/protocol/response'

const TIMEOUT_MS = 5500

// The @vtex/api HttpClient respects Cache-Control headers and uses the provided cache.
const memoryCache = new LRUCache<string, any>({ max: 5000 })

metrics.trackCache('get-benefits', memoryCache)

// This is the configuration for clients available in `ctx.clients`.
const clients: ClientsConfig<Clients> = {
  // We pass our custom implementation of the clients bag, containing the Status client.
  implementation: Clients,
  options: {
    // All IO Clients will be initialized with these options, unless otherwise specified.
    default: {
      retries: 2,
      timeout: TIMEOUT_MS,
    },
  },
}

declare global {
  // We declare a global Context type just to avoid re-writing ServiceContext<Clients, State> in every handler and resolver
  type Context = ServiceContext<Clients, State>

  interface InstalledAppEvent extends EventContext<Clients> {
    body: { id?: string }
  }

  interface State extends RecorderState {
    orderForm: CheckoutOrderForm
    orderFormId?: string
    appSettings: { externalEndpoint?: string }
    externalProviderResponse: ExternalPromotionsResponseProtocol
    parsedRequestProtocol: ExternalPromotionsRequestProtocol
  }
}

// Export a service that defines route handlers and client options.
export default new Service({
  clients,
  routes: {
    orderFormNotification: method({
      POST: [
        getAppConfiguration,
        getOrderFormById,
        calculateExternalBenefits,
        applyManualPrices,
      ],
    }),
    simulation: method({
      POST: [getAppConfiguration, calculateExternalBenefits],
    }),
  },
  events: {
    onAppInstalled: setupAppConfiguration,
    onSettingsChanged: setupAppConfiguration,
  },
})

```


[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 external-promotion-provider-middleware/node/clients/externalProvider.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { InstanceOptions, IOContext } from '@vtex/api'
import { ExternalClient } from '@vtex/api'

import type { ExternalPromotionsRequestProtocol } from '../typings/protocol/request'
import type { ExternalPromotionsResponseProtocol } from '../typings/protocol/response'

const TIMEOUT_EXTERNAL = 12500

export default class ExternalProvider extends ExternalClient {
  constructor(context: IOContext, options?: InstanceOptions) {
    super(context.settings.externalEndpoint, context, options)
  }

  public async getBenefits(
    payload: ExternalPromotionsRequestProtocol
  ): Promise<ExternalPromotionsResponseProtocol> {
    try {
      const response = await this.http.post<ExternalPromotionsResponseProtocol>(
        '/',
        payload,
        {
          metric: 'get-benefits',
          timeout: TIMEOUT_EXTERNAL,
          timeoutErrorMessage:
            'Timeout waiting for response from external provider',
          retries: 0,
        }
      )

      return response
    } catch (err) {
      throw new Error(
        `External endpoint (${this.context.settings.externalEndpoint}) returned error. ${err}`
      )
    }
  }
}

```


[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 external-promotion-provider-middleware/node/clients/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients } from '@vtex/api'
import { Checkout } from '@vtex/clients'

import ExternalProvider from './externalProvider'

// Extend the default IOClients implementation with our own custom clients.
export class Clients extends IOClients {
  public get checkout() {
    return this.getOrSet('checkout', Checkout)
  }

  public get externalProvider() {
    return this.getOrSet('externalProvider', ExternalProvider)
  }
}

```


[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 external-promotion-provider-middleware/node/constants/orderFormConfiguration.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

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

export const orderFormConfigurationConstant = {
  allowManualPrice: true,
  apps: [
    {
      fields: ['externalPromotions'],
      id: 'promotion-provider-middleware',
      major: 0,
    },
  ],
} as OrderFormConfiguration

```


[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 external-promotion-provider-middleware/node/typings/global.d.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface CheckoutOrderForm {
  orderFormId: string
  salesChannel: string
  loggedIn: boolean
  isCheckedIn: boolean
  storeId?: any
  checkedInPickupPointId?: any
  allowManualPrice: boolean
  canEditData: boolean
  userProfileId: string | null
  userType?: any
  ignoreProfileData: boolean
  value: number
  messages: Message[]
  items: Item[]
  selectableGifts: any[]
  totalizers: Totalizer[]
  shippingData: ShippingData
  clientProfileData: ClientProfileData
  paymentData: PaymentData
  marketingData?: any
  sellers: Seller[]
  clientPreferencesData: ClientPreferencesData
  commercialConditionData?: any
  storePreferencesData: StorePreferencesData
  giftRegistryData?: any
  openTextField?: any
  invoiceData?: any
  customData: CustomData
  itemMetadata: ItemMetadata
  hooksData?: any
  ratesAndBenefitsData: RatesAndBenefitsData
  subscriptionData: SubscriptionData
  itemsOrdination?: any
}

export interface Message {
  code?: any
  text: string
  status: string
  fields: any
}

export interface AdditionalInfo {
  dimension?: any
  brandName: string
  brandId: string
  offeringInfo?: any
  offeringType?: any
  offeringTypeId?: any
}

export interface ProductCategories {
  [key: string]: string
}

export interface Content {
  [key: string]: any
}

export interface Attachment {
  name: string
  content: Content
}

export interface Schema {
  [key: string]: any
}

export interface AttachmentOffering {
  name: string
  required: boolean
  schema: Schema
}

export interface MatchedParameters {
  [key: string]: any
}

export interface RatesAndBenefitsIdentifier {
  id: string
  name: string
  featured: boolean
  description?: any
  matchedParameters: MatchedParameters
  additionalInfo?: any
}

export interface PriceTag {
  name: string
  value: number
  rawValue: number
  isPercentual: boolean
  identifier: string
  ratesAndBenefitsIdentifier: RatesAndBenefitsIdentifier
}

export interface SellingPrice {
  value: number
  quantity: number
}

export interface PriceDefinition {
  calculatedSellingPrice: number
  total: number
  sellingPrices: SellingPrice[]
}

export interface Item {
  uniqueId?: string
  id: string
  productId?: string
  productRefId?: string
  refId?: string
  ean?: string
  name?: string
  skuName?: string
  modalType?: any
  parentItemIndex?: any
  parentAssemblyBinding?: any
  assemblies?: any[]
  priceValidUntil?: Date
  tax?: number
  price?: number
  listPrice?: number
  manualPrice?: number
  manualPriceAppliedBy?: string
  sellingPrice?: number
  rewardValue?: number
  isGift?: boolean
  additionalInfo?: AdditionalInfo
  preSaleDate?: any
  productCategoryIds?: string
  productCategories?: ProductCategories
  quantity: number
  seller?: string
  sellerChain?: string[]
  imageUrl?: string
  detailUrl?: string
  components?: any[]
  bundleItems?: any[]
  attachments?: Attachment[]
  attachmentOfferings?: AttachmentOffering[]
  offerings?: any[]
  priceTags?: PriceTag[]
  availability?: string
  measurementUnit?: string
  unitMultiplier?: number
  manufacturerCode?: any
  priceDefinition?: PriceDefinition
}

export interface Totalizer {
  id: string
  name: string
  value: number
}

export interface Address {
  addressType: string
  receiverName: string
  addressId: string
  isDisposable: boolean
  postalCode: string
  city: string
  state: string
  country: string
  street: string
  number: string
  neighborhood: string
  complement: string
  reference?: any
  geoCoordinates: any[]
}

export interface DeliveryId {
  courierId: string
  warehouseId: string
  dockId: string
  courierName: string
  quantity: number
  kitItemDetails: any[]
}

export interface PickupStoreInfo {
  isPickupStore: boolean
  friendlyName?: any
  address?: any
  additionalInfo?: any
  dockId?: any
}

export interface Sla {
  id: string
  deliveryChannel: string
  name: string
  deliveryIds: DeliveryId[]
  shippingEstimate: string
  shippingEstimateDate?: any
  lockTTL?: any
  availableDeliveryWindows: any[]
  deliveryWindow?: any
  price: number
  listPrice: number
  tax: number
  pickupStoreInfo: PickupStoreInfo
  pickupPointId?: any
  pickupDistance: number
  polygonName?: any
  transitTime: string
}

export interface DeliveryChannel {
  id: string
}

export interface LogisticsInfo {
  itemIndex: number
  selectedSla: string
  selectedDeliveryChannel: string
  addressId: string
  slas: Sla[]
  shipsTo: string[]
  itemId: string
  deliveryChannels: DeliveryChannel[]
}

export interface SelectedAddress {
  addressType: string
  receiverName: string
  addressId: string
  isDisposable: boolean
  postalCode: string
  city: string
  state: string
  country: string
  street: string
  number: string
  neighborhood: string
  complement: string
  reference?: any
  geoCoordinates: any[]
}

export interface AvailableAddress {
  addressType: string
  receiverName: string
  addressId: string
  isDisposable: boolean
  postalCode: string
  city: string
  state: string
  country: string
  street: string
  number: string
  neighborhood: string
  complement: string
  reference?: any
  geoCoordinates: any[]
}

export interface ShippingData {
  address: Address
  logisticsInfo: LogisticsInfo[]
  selectedAddresses: SelectedAddress[]
  availableAddresses: AvailableAddress[]
  pickupPoints: any[]
}

export interface ClientProfileData {
  email: string
  firstName: string
  lastName: string
  document: string
  documentType: string
  phone: string
  corporateName?: any
  tradeName?: any
  corporateDocument?: any
  stateInscription?: any
  corporatePhone?: any
  isCorporate: boolean
  profileCompleteOnLoading: boolean
  profileErrorOnLoading: boolean
  customerClass?: any
}

export interface SellerMerchantInstallment {
  id: string
  count: number
  hasInterestRate: boolean
  interestRate: number
  value: number
  total: number
}

export interface Installment {
  count: number
  hasInterestRate: boolean
  interestRate: number
  value: number
  total: number
  sellerMerchantInstallments: SellerMerchantInstallment[]
}

export interface InstallmentOption {
  paymentSystem: string
  bin: string
  paymentName?: any
  paymentGroupName?: any
  value: number
  installments: Installment[]
}

export interface Validator {
  regex: string
  mask: string
  cardCodeRegex: string
  cardCodeMask: string
  weights: number[]
  useCvv: boolean
  useExpirationDate: boolean
  useCardHolderName: boolean
  useBillingAddress: boolean
}

export interface PaymentSystem {
  id: number
  name: string
  groupName: string
  validator: Validator
  stringId: string
  template: string
  requiresDocument: boolean
  isCustom: boolean
  description?: any
  requiresAuthentication: boolean
  dueDate: Date
  availablePayments?: any
}

export interface MerchantSellerPayment {
  id: string
  installments: number
  referenceValue: number
  value: number
  interestRate: number
  installmentValue: number
}

export interface Payment {
  paymentSystem: string
  bin: string
  accountId: string
  tokenId?: any
  installments: number
  referenceValue: number
  value: number
  merchantSellerPayments: MerchantSellerPayment[]
}

export interface AvailableAccount {
  accountId: string
  paymentSystem: string
  paymentSystemName: string
  cardNumber: string
  bin: string
  availableAddresses: string[]
  expirationDate: string
  isExpired: boolean
}

export interface PaymentData {
  updateStatus: string
  installmentOptions: InstallmentOption[]
  paymentSystems: PaymentSystem[]
  payments: Payment[]
  giftCards: any[]
  giftCardMessages: any[]
  availableAccounts: AvailableAccount[]
  availableTokens: any[]
}

export interface Seller {
  id: string
  name: string
  logo: string
}

export interface ClientPreferencesData {
  locale: string
  optinNewsLetter: boolean
}

export interface CurrencyFormatInfo {
  currencyDecimalDigits: number
  currencyDecimalSeparator: string
  currencyGroupSeparator: string
  currencyGroupSize: number
  startsWithCurrencySymbol: boolean
}

export interface StorePreferencesData {
  countryCode: string
  saveUserData: boolean
  timeZone: string
  currencyCode: string
  currencyLocale: number
  currencySymbol: string
  currencyFormatInfo: CurrencyFormatInfo
}

export interface CustomApp {
  fields: any
  id: string
  major: number
}

export interface CustomData {
  customApps: CustomApp[]
}

export interface InputValues {
  [key: string]: any
}

export interface AssemblyOption {
  id: string
  name: string
  required: boolean
  inputValues: InputValues
  composition?: any
}

export interface ItemMetadata {
  items: any[]
}

export interface RateAndBenefitsIdentifier {
  id: string
  name: string
  featured: boolean
  description?: any
  matchedParameters: MatchedParameters
  additionalInfo?: any
}

export interface RatesAndBenefitsData {
  rateAndBenefitsIdentifiers: RateAndBenefitsIdentifier[]
  teaser: any[]
}

export interface Subscription {
  itemIndex: number
  plan: any
  executionCount: number
}

export interface SubscriptionData {
  subscriptions: Subscription[]
}

```


[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 external-promotion-provider-middleware/node/middlewares/calculateExternalBenefits.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { json } from 'co-body'

import { parseOrderFormToProtocol } from '../utils/index'

const calculateExternalBenefits = async (
  ctx: Context,
  next: () => Promise<any>
) => {
  try {
    const simulationRequestBody = await json(ctx.req)

    ctx.state.parsedRequestProtocol = parseOrderFormToProtocol(
      ctx.state.orderForm ?? simulationRequestBody
    )

    ctx.state.externalProviderResponse = await ctx.clients.externalProvider.getBenefits(
      ctx.state.parsedRequestProtocol
    )
  } catch (err) {
    ctx.vtex.logger.error({
      calculateExternalBenefits: {
        status: 'failed',
        content: err.message,
      },
    })

    ctx.status = 400
    ctx.body = {
      error: err.message,
    }

    return
  }

  if (ctx.vtex.route.id === 'simulation') {
    ctx.status = 200
    ctx.body = ctx.state.externalProviderResponse

    return
  }

  const {
    orderForm: {
      items,
      totalizers,
      clientProfileData,
      shippingData,
      paymentData: { payments },
      customData,
    },
    externalProviderResponse,
  } = ctx.state

  ctx.vtex.logger.info({
    calculateExternalBenefits: {
      status: 'External endpoint successfully returned',
      content: {
        orderFormId: ctx.state.orderFormId,
        orderForm: {
          items,
          totalizers,
          clientProfileData,
          shippingData,
          payments,
          customData,
        },
        externalProviderResponse,
      },
    },
  })

  if (
    Array.isArray(ctx.state.externalProviderResponse.items) &&
    !ctx.state.externalProviderResponse.items.length
  ) {
    ctx.status = 200
    ctx.body = {
      message:
        'No promotions applied, Items array was sent empty by the external provider',
    }

    return
  }

  await next()
}

export default calculateExternalBenefits

```


[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 external-promotion-provider-middleware/node/middlewares/setupAppConfiguration.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { orderFormConfigurationConstant } from '../constants/orderFormConfiguration'
import { orderFormConfiguration } from '../services'

const setupAppConfiguration = async (
  ctx: InstalledAppEvent,
  next: () => Promise<any>
) => {
  const { checkout } = ctx.clients

  try {
    await orderFormConfiguration.setConfiguration({
      client: checkout,
      configuration: orderFormConfigurationConstant,
    })
  } catch (error) {
    ctx.vtex.logger.error({
      setupAppConfigurationError: {
        status: 'failed',
        content: error,
      },
    })
    throw new Error("Couldn't set orderform configuration")
  }

  await next()
}

export default setupAppConfiguration

```


[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 external-promotion-provider-middleware/node/middlewares/applyManualPrices.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { applyApportionment, validateProviderResponse } from '../utils'
import { customData } from '../services'

const applyManualPrices = async (ctx: Context, next: () => Promise<any>) => {
  try {
    await validateProviderResponse.schemaConsistency(
      ctx.state.externalProviderResponse
    )

    validateProviderResponse.indexConsistency(
      ctx.state.orderForm,
      ctx.state.externalProviderResponse
    )

    const apportionedPayload = applyApportionment(
      ctx.state.parsedRequestProtocol,
      ctx.state.externalProviderResponse
    )

    await ctx.clients.checkout.updateItems(
      ctx.state.orderFormId as string,
      apportionedPayload,
      'AUTH_TOKEN'
    )

    await customData.setCustomData({
      client: ctx.clients.checkout,
      orderForm: ctx.state.orderForm,
      externalProviderResponse: ctx.state.externalProviderResponse,
    })

    ctx.status = 204
  } catch (err) {
    ctx.vtex.logger.error({
      applyManualPrices: {
        status: 'failed',
        content: {
          error: err.message,
          orderForm: ctx.state.orderForm,
          externalProviderResponse: ctx.state.externalProviderResponse,
        },
      },
    })
    ctx.status = 400
    ctx.body = { error: err.message }

    return
  }

  await next()
}

export default applyManualPrices

```


[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 external-promotion-provider-middleware/node/middlewares/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import applyManualPrices from './applyManualPrices'
import calculateExternalBenefits from './calculateExternalBenefits'
import getOrderFormById from './getOrderFormById'
import setupAppConfiguration from './setupAppConfiguration'
import getAppConfiguration from './getAppConfiguration'

export {
  setupAppConfiguration,
  getOrderFormById,
  applyManualPrices,
  calculateExternalBenefits,
  getAppConfiguration,
}

```


[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 external-promotion-provider-middleware/node/middlewares/getOrderFormById.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { customData, orderform } from '../services'
import { getOrderFormId } from '../utils'

const getOrderFormById = async (ctx: Context, next: () => Promise<any>) => {
  try {
    ctx.state.orderFormId = getOrderFormId(ctx.cookies)

    const currentOrderform = await orderform.getOrderFormById(
      ctx.clients.checkout,
      ctx.state.orderFormId as string
    )

    ctx.state.orderForm = await orderform.deleteAllManualPrices(
      ctx.clients.checkout,
      ctx.vtex.logger,
      currentOrderform
    )

    // this method is necessary to clean customData
    await customData.setCustomData({
      client: ctx.clients.checkout,
      orderForm: ctx.state.orderForm,
      externalProviderResponse: null,
    })
  } catch (error) {
    ctx.vtex.logger.error({
      getOrderFormById: {
        status: 'failed',
        error: error.message,
        orderFormId: ctx.state.orderFormId,
      },
    })
    ctx.status = 400
    ctx.body = { error: error.message }

    return
  }

  await next()
}

export default getOrderFormById

```


[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 external-promotion-provider-middleware/node/middlewares/getAppConfiguration.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
type IAppSettings = { externalEndpoint: string }

const getAppConfiguration = async (ctx: Context, next: () => Promise<any>) => {
  const {
    clients: { apps },
    vtex: { account, workspace },
  } = ctx

  try {
    const appSettings: IAppSettings = await apps.getAppSettings(
      process.env.VTEX_APP_ID as string
    )

    ctx.vtex.settings = { ...ctx.vtex.settings, ...appSettings }

    if (!ctx.vtex.settings.externalEndpoint) {
      throw new Error(
        `No external endpoint was set. Go to https://${workspace}--${account}.myvtex.com/admin/apps/${process.env.VTEX_APP_ID}/setup/ and set your external endpoint`
      )
    }
  } catch (error) {
    ctx.vtex.logger.error({
      getAppSettingsError: {
        status: 'failed',
        content: error,
      },
    })

    ctx.status = 400
    ctx.body = { error: error.message }

    return
  }

  await next()
}

export default getAppConfiguration

```


[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 external-promotion-provider-middleware/node/utils/getOrderFormId.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type Cookies from 'cookies'

export function getOrderFormId(cookies: Cookies) {
  const orderFormId = cookies.get('checkout.vtex.com')?.slice(7)

  if (!orderFormId) {
    throw new Error(
      'No checkout.vtex.com cookie containing the orderformId was sent'
    )
  }

  return orderFormId
}

```


[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 external-promotion-provider-middleware/node/utils/applyApportionment.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  ExternalPromotionsRequestProtocol,
  Item,
} from '../typings/protocol/request'
import type {
  ExternalPromotionsResponseProtocol,
  Variation,
  ExternalPromotion,
} from '../typings/protocol/response'

export function getFlattenedExternalPromotions(itemVariations: Variation[]) {
  return itemVariations.reduce<ExternalPromotion[]>(
    (memo, variation) => memo.concat(variation.externalPromotions),
    []
  )
}

// this function is important for cases where the SKU has split indexes
function getItemQuantityAndPrice(requestItems: Item[], skuId: number) {
  const filteredItem = requestItems.filter((item: Item) => {
    return Number(item.id) === skuId
  })

  if (filteredItem.length > 1) {
    throw new Error(`Multiple items with SKUID ${skuId}`)
  }

  const { price } = filteredItem[0].variations[0]

  const totalQuantity = filteredItem[0].variations.reduce(
    (summedQuantity, variation) => {
      return summedQuantity + variation.quantity
    },
    0
  )

  return { price, totalQuantity }
}

export function applyApportionment(
  parsedRequestProtocol: ExternalPromotionsRequestProtocol,
  providerResponse: ExternalPromotionsResponseProtocol
) {
  const apportionedPayload = providerResponse.items.map((item) => {
    const skuId = item.id

    const flattenedExternalPromotions = getFlattenedExternalPromotions(
      item.variations
    )

    const discount = flattenedExternalPromotions.reduce(
      (discountSum, externalPromotion) => {
        if (externalPromotion.isPercentual) {
          return {
            percentualDiscount:
              discountSum.percentualDiscount + externalPromotion.value,
            nominalDiscount: discountSum.nominalDiscount,
          }
        }

        return {
          percentualDiscount: discountSum.percentualDiscount,
          nominalDiscount:
            discountSum.nominalDiscount + externalPromotion.value,
        }
      },
      {
        percentualDiscount: 0,
        nominalDiscount: 0,
      }
    )

    const { totalQuantity, price } = getItemQuantityAndPrice(
      parsedRequestProtocol.items,
      skuId
    )

    const totalPrice = price * totalQuantity
    const netTotalPrice =
      totalPrice * (1 - discount.percentualDiscount) + discount.nominalDiscount

    const unitPrice = Math.ceil(netTotalPrice / totalQuantity)

    return {
      index: item.variations[0].requestIndex,
      price: unitPrice,
    }
  })

  return apportionedPayload
}

```


[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 external-promotion-provider-middleware/node/utils/validateProviderResponse.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import schemas from '../schemas'
import type { CheckoutOrderForm } from '../typings/global'
import type { ExternalPromotionsResponseProtocol } from '../typings/protocol/response'

async function schemaConsistency(
  providerResponse: ExternalPromotionsResponseProtocol
) {
  await schemas.providerResponseSchema
    .validate(providerResponse, {
      strict: true,
      abortEarly: false,
    })
    .catch((err) => {
      throw new Error(err.errors.join('\n\n'))
    })
}

function indexConsistency(
  orderForm: CheckoutOrderForm,
  providerResponse: ExternalPromotionsResponseProtocol
) {
  const { items: orderFormItems } = orderForm
  const { items } = providerResponse

  items.forEach((item) => {
    const { id, variations } = item

    variations.forEach((variation) => {
      const { requestIndex } = variation

      if (Number(orderFormItems[requestIndex]?.id) !== id) {
        throw new Error(
          `RequestIndex from response does not match orderForm's original index for skuId ${id}`
        )
      }
    })
  })
}

export default { schemaConsistency, indexConsistency }

```


[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 external-promotion-provider-middleware/node/utils/parseOrderFormToProtocol.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type {
  CheckoutOrderForm,
  Attachment,
  AttachmentOffering,
  PriceTag,
} from '../typings/global'
import type { ExternalPromotionsRequestProtocol } from '../typings/protocol/request'

interface INonVariableFields {
  id: string
  productId?: string
  refId?: string
  ean?: string
  name?: string
  skuName?: string
  modalType?: any
  productCategoryIds?: string
  productCategories?: { [key: string]: string }
  availability?: string
  measurementUnit?: string
}

interface IVariableFields {
  index: number
  assemblies?: any[]
  tax?: number
  price?: number
  listPrice?: number
  manualPrice?: number | undefined
  sellingPrice?: number
  isGift?: boolean
  quantity?: number
  attachments?: Attachment[]
  attachmentOfferings?: AttachmentOffering[]
  offerings?: any[]
  priceTags?: PriceTag[]
  unitMultiplier?: number
  seller?: string
  sellerChain?: string[]
}

export function parseOrderFormToProtocol({
  items,
  customData,
  shippingData,
  clientProfileData,
  marketingData,
  paymentData,
  totalizers,
}: CheckoutOrderForm): ExternalPromotionsRequestProtocol {
  if (!items) {
    throw new Error('Property "items" is obligatory')
  }

  const [itemsVariableFields, itemsNonVariableFields] = items.reduce(
    (
      [variableFields, nonVariableFields]: [
        IVariableFields[],
        INonVariableFields[]
      ],
      item,
      index
    ) => {
      if (!item.id) {
        throw new Error(
          `Property "id" is missing in index ${index} inside Items Array (this property is obligatory)`
        )
      }

      nonVariableFields.push({
        id: item.id,
        productId: item.productId,
        refId: item.refId,
        ean: item.ean,
        name: item.name,
        skuName: item.skuName,
        modalType: item.modalType,
        productCategoryIds: item.productCategoryIds,
        productCategories: item.productCategories,
        availability: item.availability,
        measurementUnit: item.measurementUnit,
      })

      variableFields.push({
        index,
        assemblies: item.assemblies,
        tax: item.tax,
        price: item.price,
        listPrice: item.listPrice,
        manualPrice: item.manualPrice,
        sellingPrice: item.sellingPrice,
        isGift: item.isGift,
        quantity: item.quantity,
        attachments: item.attachments,
        attachmentOfferings: item.attachmentOfferings,
        offerings: item.offerings,
        priceTags: item.priceTags,
        unitMultiplier: item.unitMultiplier,
        seller: item.seller,
        sellerChain: item.sellerChain,
      })

      return [variableFields, nonVariableFields]
    },
    [[], []]
  )

  const formattedItems = itemsNonVariableFields.reduce(
    (memo: any[], itemNonVariableFields, index) => {
      if (
        index &&
        itemsNonVariableFields[index - 1]?.id === itemNonVariableFields.id
      ) {
        const compiledVariations = [
          ...memo[memo.length - 1].variations,
          itemsVariableFields[index],
        ]

        const newItem = {
          ...itemNonVariableFields,
          variations: compiledVariations,
        }

        memo.pop()

        return [...memo, newItem]
      }

      return memo.concat({
        ...itemNonVariableFields,
        variations: [itemsVariableFields[index]],
      })
    },
    []
  )

  const parsedOrderFormToProtocol = {
    items: formattedItems,
    customData,
    shippingData: {
      logisticsInfo: shippingData?.logisticsInfo,
      selectedAddresses: shippingData?.selectedAddresses,
    },
    clientProfileData,
    marketingData,
    paymentData: { payments: paymentData?.payments },
    totalizers,
  }

  return parsedOrderFormToProtocol
}

```


[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 external-promotion-provider-middleware/node/utils/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { parseOrderFormToProtocol } from './parseOrderFormToProtocol'
import { getOrderFormId } from './getOrderFormId'
import {
  applyApportionment,
  getFlattenedExternalPromotions,
} from './applyApportionment'
import validateProviderResponse from './validateProviderResponse'

export {
  parseOrderFormToProtocol,
  getOrderFormId,
  applyApportionment,
  validateProviderResponse,
  getFlattenedExternalPromotions,
}

```


[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 external-promotion-provider-middleware/node/schemas/providerResponse.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import * as yup from 'yup'
import mapValues from 'lodash/mapValues'

export const providerResponseSchema = yup.object().shape({
  items: yup
    .array()
    .of(
      yup.object().shape({
        id: yup.number().required(),
        variations: yup
          .array()
          .of(
            yup.object().shape({
              requestIndex: yup.number().required(),
              quantity: yup.number().required(),
              externalPromotions: yup
                .array()
                .of(
                  yup.object().shape({
                    identifier: yup.string(),
                    isPercentual: yup.boolean().required(),
                    value: yup.number().required(),
                    matchedParameters: yup.lazy((obj) =>
                      yup.object(
                        mapValues(obj, (value, key) => {
                          return yup
                            .string()
                            .required()
                            .test(
                              'length',
                              `Key ${key} inside matchedParameters must have less than (or equal) 50 characters.`,
                              () => Number(key?.length) <= 50
                            )
                            .test(
                              'length',
                              `Value ${value} in key ${key} (matchedParameters array) must have less than (or equal) 50 characters.`,
                              (val) => Number(val?.length) <= 50
                            )
                        })
                      )
                    ),
                  })
                )
                .required()
                .test({
                  message:
                    'externalPromotions array should have at least one object.',
                  test: (arr) => !!arr?.length,
                }),
            })
          )
          .required()
          .test({
            message: 'variations array should have at least one object.',
            test: (arr) => !!arr?.length,
          }),
      })
    )
    .required(),
})

```


[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 external-promotion-provider-middleware/node/schemas/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { providerResponseSchema } from './providerResponse'

export default { providerResponseSchema }

```


[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 external-promotion-provider-middleware/node/services/customData.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

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

import type { CheckoutOrderForm } from '../typings/global'
import type { ExternalPromotionsResponseProtocol } from '../typings/protocol/response'
import { getFlattenedExternalPromotions } from '../utils'

type Args = {
  client: Checkout
  orderForm: CheckoutOrderForm
  externalProviderResponse: ExternalPromotionsResponseProtocol | null
}

const setCustomData = async ({
  client,
  orderForm,
  externalProviderResponse,
}: Args) => {
  const targetCustomAppId = 'promotion-provider-middleware'

  const providedExternalPromotions = externalProviderResponse?.items.map(
    (item) => {
      return {
        id: item.id,
        externalPromotions: getFlattenedExternalPromotions(item.variations),
      }
    }
  )

  const value = providedExternalPromotions
    ? JSON.stringify(providedExternalPromotions)
    : '[]'

  try {
    await client.setSingleCustomData(
      orderForm.orderFormId,
      {
        appId: targetCustomAppId,
        appFieldName: 'externalPromotions',
        value,
      },
      'AUTH_TOKEN'
    )
  } catch (error) {
    throw new Error(`It was not possible to set customData. ${error}`)
  }
}

export default { setCustomData }

```


[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 external-promotion-provider-middleware/node/services/orderform.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

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

import type { CheckoutOrderForm } from '../typings/global'

const getOrderFormById = async (client: Checkout, orderFormId: string) => {
  try {
    const orderForm: CheckoutOrderForm = ((await client.orderForm(
      orderFormId
    )) as unknown) as CheckoutOrderForm

    return orderForm
  } catch (error) {
    throw new Error(`Error when fetching orderform. Returned error: ${error}`)
  }
}

async function deleteAllManualPrices(
  client: Checkout,
  logger: Logger,
  orderForm: CheckoutOrderForm
) {
  const { items, orderFormId } = orderForm

  if (!items.length) {
    throw new Error("Found no items inside orderForm's items array")
  }

  const orderItems = items.map((_, index) => {
    return {
      index,
      price: null,
    }
  })

  try {
    const orderFormWithoutManualPrices = ((await client.updateItems(
      orderFormId,
      orderItems,
      'AUTH_TOKEN'
    )) as unknown) as CheckoutOrderForm

    return orderFormWithoutManualPrices
  } catch (error) {
    logger.error({ error })
    throw new Error(
      `Error when removing all manual prices from orderform. Returned error: ${error}`
    )
  }
}

export default { getOrderFormById, deleteAllManualPrices }

```


[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 external-promotion-provider-middleware/node/services/orderFormConfiguration.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import type { App, Checkout, OrderFormConfiguration } from '@vtex/clients'

type Args = {
  client: Checkout
  configuration: OrderFormConfiguration
}

const getCurrentConfiguration = async (client: Checkout) => {
  const currentOrderformConfiguration = await client.getOrderFormConfiguration()
  const isAppAlreadySet = currentOrderformConfiguration.apps.some(
    (app: App) => app.id === 'promotion-provider-middleware'
  )

  return {
    isAppAlreadySet,
    isManualPriceTrue: currentOrderformConfiguration.allowManualPrice,
    currentOrderformConfiguration,
  }
}

const setConfiguration = async ({
  client,
  configuration,
}: Args): Promise<void> => {
  const {
    isAppAlreadySet,
    isManualPriceTrue,
    currentOrderformConfiguration,
  } = await getCurrentConfiguration(client)

  if (isManualPriceTrue && isAppAlreadySet) {
    return
  }

  try {
    await client.setOrderFormConfiguration({
      ...currentOrderformConfiguration,
      apps: [
        ...currentOrderformConfiguration.apps,
        ...(isAppAlreadySet ? [] : configuration.apps),
      ],
      allowManualPrice: configuration.allowManualPrice,
    })
  } catch (e) {
    throw new Error('Error while setting orderform configuration')
  }
}

export default { setConfiguration, getCurrentConfiguration }

```


[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 external-promotion-provider-middleware/node/services/index.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import customData from './customData'
import orderFormConfiguration from './orderFormConfiguration'
import orderform from './orderform'

export { customData, orderFormConfiguration, orderform }

```


[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 external-promotion-provider-middleware/node/typings/protocol/request.d.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface ExternalPromotionsRequestProtocol {
  items: Item[]
  customData: CustomData
  shippingData: ShippingData
  clientProfileData: ClientProfileData
  marketingData?: MarketingData
  paymentData: PaymentData
  totalizers: Totalizer[]
}

export interface Totalizer {
  id: string
  name: string
  value: number
}

export interface MarketingData {
  utmSource?: string
  utmMedium?: string
  utmCampaign?: string
  utmipage?: string
  utmiPart?: string
  utmiCampaign?: string
  coupon?: string
  marketingTags: any[]
}

export interface ProductCategories {
  [key: string]: string
}
export interface Content {
  [key: string]: any
}

export interface Attachment {
  name: string
  content: Content
}

export interface AttachmentOffering {
  name: string
  required: boolean
  schema: any
}

export interface PriceTag {
  name: string
  value: number
  rawValue: number
  isPercentual: boolean
  identifier: string
}

export interface Variation {
  index: number
  assemblies: any[]
  tax: number
  price: number
  listPrice: number
  manualPrice?: any
  sellingPrice: number
  isGift: boolean
  quantity: number
  attachments: Attachment[]
  attachmentOfferings: AttachmentOffering[]
  offerings: any[]
  priceTags: PriceTag[]
  unitMultiplier: number
  seller: string
  sellerChain: string[]
}

export interface Item {
  id: string
  productId: string
  refId?: any
  ean: string
  name: string
  skuName: string
  modalType?: any
  productCategoryIds: string
  productCategories: ProductCategories
  availability: string
  measurementUnit: string
  variations: Variation[]
}

export interface Fields {
  [key: string]: string
}

export interface CustomApp {
  fields: Fields
  id: string
  major: number
}

export interface CustomData {
  customApps: CustomApp[]
}

export interface DeliveryId {
  courierId: string
  warehouseId: string
  dockId: string
  courierName: string
  quantity: number
  kitItemDetails: any[]
}

export interface PickupStoreInfo {
  isPickupStore: boolean
  friendlyName?: any
  address?: any
  additionalInfo?: any
  dockId?: any
}

export interface Sla {
  id: string
  deliveryChannel: string
  name: string
  deliveryIds: DeliveryId[]
  shippingEstimate: string
  shippingEstimateDate?: any
  lockTTL?: any
  availableDeliveryWindows: any[]
  deliveryWindow?: any
  price: number
  listPrice: number
  tax: number
  pickupStoreInfo: PickupStoreInfo
  pickupPointId?: any
  pickupDistance: number
  polygonName?: any
  transitTime: string
}

export interface DeliveryChannel {
  id: string
}

export interface LogisticsInfo {
  itemIndex: number
  selectedSla: string
  selectedDeliveryChannel: string
  addressId: string
  slas: Sla[]
  shipsTo: string[]
  itemId: string
  deliveryChannels: DeliveryChannel[]
}

export interface SelectedAddress {
  addressType: string
  receiverName: string
  addressId: string
  isDisposable: boolean
  postalCode: string
  city: string
  state: string
  country: string
  street: string
  number: string
  neighborhood: string
  complement: string
  reference?: any
  geoCoordinates: any[]
}

export interface ShippingData {
  logisticsInfo: LogisticsInfo[]
  selectedAddresses: SelectedAddress[]
}

export interface ClientProfileData {
  email: string
  firstName: string
  lastName: string
  document: string
  documentType: string
  phone: string
  corporateName?: any
  tradeName?: any
  corporateDocument?: any
  stateInscription?: any
  corporatePhone?: any
  isCorporate: boolean
  profileCompleteOnLoading: boolean
  profileErrorOnLoading: boolean
  customerClass?: any
}

export interface MerchantSellerPayment {
  id: string
  installments: number
  referenceValue: number
  value: number
  interestRate: number
  installmentValue: number
}

export interface Payment {
  paymentSystem: string
  bin: string
  accountId: string
  tokenId?: any
  installments: number
  referenceValue: number
  value: number
  merchantSellerPayments: MerchantSellerPayment[]
}

export interface PaymentData {
  payments: Payment[]
}

```


[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 external-promotion-provider-middleware/node/typings/protocol/response.d.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
export interface ExternalPromotionsResponseProtocol {
  items: Item[]
}

export interface MatchedParameters {
  [key: string]: string
}

export interface ExternalPromotion {
  matchedParameters: MatchedParameters
  identifier: string
  isPercentual: boolean
  value: number
}

export interface Variation {
  requestIndex: number
  quantity: number
  externalPromotions: ExternalPromotion[]
}

export interface Item {
  id: number
  variations: Variation[]
}

```


[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 external-promotion-provider-middleware/node/__tests__/unit/middlewares/getOrderFormById.spec.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { getOrderFormById } from '../../../middlewares'

describe('getOrderFormById', () => {
  it('should return error when fecthing the orderform fails', async () => {
    const ctx = {
      body: '',
      state: { orderform: {}, orderFormId: {} },
      cookies: {
        get: jest
          .fn()
          .mockReturnValueOnce('__ofid=03db3a179c7d4a9085152068b555eee1'),
      },
      vtex: { logger: { error: jest.fn() } },
      clients: { checkout: { orderForm: jest.fn().mockRejectedValueOnce('') } },
    } as any

    const next = jest.fn()

    await getOrderFormById(ctx, next)

    expect(ctx.body).toMatchObject({ error: 'Error when fetching orderform' })
  })
  it('should return error when deleting all manual prices fails', async () => {
    const ctx = {
      body: '',
      state: { orderform: {}, orderFormId: {} },
      cookies: {
        get: jest
          .fn()
          .mockReturnValueOnce('__ofid=03db3a179c7d4a9085152068b555eee1'),
      },
      vtex: { logger: { error: jest.fn() } },
      clients: {
        checkout: {
          orderForm: jest.fn().mockResolvedValueOnce({ items: [] }),
          updateItems: jest.fn().mockRejectedValueOnce(''),
        },
      },
    } as any

    const next = jest.fn()

    await getOrderFormById(ctx, next)

    expect(ctx.body).toMatchObject({
      error: 'Error when removing all manual prices from orderform',
    })
  })
  it('should not call next when an error is returned', async () => {
    const ctx = {
      body: '',
      state: { orderform: {}, orderFormId: {} },
      cookies: {
        get: jest
          .fn()
          .mockReturnValueOnce('__ofid=03db3a179c7d4a9085152068b555eee1'),
      },
      vtex: { logger: { error: jest.fn() } },
      clients: {
        checkout: {
          orderForm: jest.fn().mockResolvedValueOnce({ items: [] }),
          updateItems: jest.fn().mockRejectedValueOnce(''),
        },
      },
    } as any

    const next = jest.fn()

    await getOrderFormById(ctx, next)

    expect(next).not.toBeCalled()
  })
  it('should call next when no errors are returned', async () => {
    const ctx = {
      body: '',
      state: { orderform: {}, orderFormId: {} },
      cookies: {
        get: jest
          .fn()
          .mockReturnValueOnce('__ofid=03db3a179c7d4a9085152068b555eee1'),
      },
      vtex: { logger: { error: jest.fn() } },
      clients: {
        checkout: {
          orderForm: jest.fn().mockResolvedValueOnce({ items: [] }),
          updateItems: jest.fn().mockResolvedValueOnce(''),
          setSingleCustomData: jest.fn().mockResolvedValueOnce(''),
        },
      },
    } as any

    const next = jest.fn()

    await getOrderFormById(ctx, next)

    expect(next).toBeCalled()
  })
})

```


[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 external-promotion-provider-middleware/node/__tests__/unit/middlewares/setupAppConfiguration.spec.ts from the external-promotion-provider-middleware VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { setupAppConfiguration } from '../../../middlewares'
import { orderFormConfiguration } from '../../../services'

jest.mock('../../../services')

describe('setupAppConfiguration', () => {
  const ctx = {
    state: {},
    clients: {
      apps: { getAppSettings: jest.fn() },
    },
    vtex: { logger: { error: jest.fn() } },
  } as any

  it('should return error when setConfiguration is rejected', async () => {
    jest
      .spyOn(orderFormConfiguration, 'setConfiguration')
      .mockImplementation()
      .mockRejectedValueOnce(new Error())

    const next = jest.fn()

    await expect(setupAppConfiguration(ctx, next)).rejects.toThrow(
      "Couldn't set orderform configuration"
    )
  })
  it('should call next function', async () => {
    jest.spyOn(orderFormConfiguration, 'setConfiguration').mockImplementation()

    const next = jest.fn()

    await setupAppConfiguration(ctx, next)
    expect(next).toHaveBeenCalled()
  })
})

```