We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more

Shopify Function Error tags are not defined

Shopify Function Error tags are not defined

nyashamadzokere
Shopify Partner
9 0 1
I am creating option for user to enter the tag they want to apply the discount on app UI.Adding discount option works.This is my code if there is anyone who can guide on what am doing wrong. 
ReferenceError: tags are not defined
query RunInput ($inputTags: [String!]!) {
  cart {
    buyerIdentity {
      customer {
        metafield(namespace: "custom", key: "pmore") {
          value
        }
      }
    }
    lines {
      quantity
      merchandise {
        ...on ProductVariant {
            id
            product{
            hasAnyTag(tags: $inputTags)
            }
            __typename
        }
      }
    }
  }
  discountNode {
    metafield(namespace: "$app:2FORDEALC", key: "function-configuration") {
      value
    }
  }
}
// @ts-nocheck
import { DiscountApplicationStrategy } from "../generated/api";

/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 * @typedef {import("../generated/api").Target} Target
 * @typedef {import("../generated/api").ProductVariant} ProductVariant
 */

/**
 * @type {FunctionRunResult}
 */
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.First,
  discounts: [],
};

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  /**
   * @type {{
   *   quantity: number
   * }}
   */
  const configuration = JSON.parse(
    input?.discountNode?.metafield?.value ?? "{}",
  );
  if (!configuration.fixedAmountOff) {
    return EMPTY_DISCOUNT;
  }


  /**
  * @type {Array<Target>}
  */
  const targets = [];

  /**
  * @type {number}
  */
  let Totalquan = 0;
  
  /**
  * @type {number}
  */
  const DisAmount = configuration.fixedAmountOff;

  /**
  * @type {number}
  */
  let TotalDiscount = 0;

  // Type check for optional chaining

    // Get total tag matches
    input.cart.lines.forEach((line) => {
      if (line.merchandise.__typename === "ProductVariant") {
        // Type check for optional chaining
        if (line.merchandise.product?.hasAnyTag) {
          Totalquan += line.quantity; // Type inference for addition
        }
      }
    });

    if (Totalquan % 2 === 0) {
      TotalDiscount = DisAmount * Totalquan;
    } else {
      TotalDiscount = DisAmount * (Totalquan - 1);
    }

    // Uneven counter
    /**
    * @type {number}
    */
    let uc = 0;

    if (Totalquan % 2 === 0) {
      // Even line item discount
      input.cart.lines.forEach((line) => {
        if (line.merchandise.__typename === "ProductVariant") {
          if (line.merchandise.product?.hasAnyTag) {
            const product = /** @type {ProductVariant} */ (line.merchandise);
            const productid = product.id;
            let productquantity = line.quantity;

            targets.push({
              productVariant: {
                id: productid,
                quantity: productquantity,
              },
            });
          }
        }
      });
    } else {
      // Uneven line item discount
      input.cart.lines.forEach((line) => {
        if (line.merchandise.__typename === "ProductVariant") {
          if (line.merchandise.product?.hasAnyTag) {
            const product = /** @type {ProductVariant} */ (line.merchandise);
            const productid = product.id;
            let productquantity = line.quantity;

            if (productquantity % 2 === 1 && uc === 0) {
              productquantity -= 1;
            }
            uc++;

            if (productquantity !== 0) {
              targets.push({
                productVariant: {
                  id: productid,
                  quantity: productquantity,
                },
              });
            }
          }
        }
      });
    }
  


  if (!targets.length) {
    console.error("No cart lines qualify for volume discount.");
    return EMPTY_DISCOUNT;
  }

  return {
    discounts: [
      {
        targets,
        value: {
          fixedAmount: {
            amount : TotalDiscount
          }
        }
      },
    ],
    discountApplicationStrategy: DiscountApplicationStrategy.First,
  };
}
import { useEffect, useMemo } from "react";
import { json } from "@remix-run/node";
import { useForm, useField } from "@shopify/react-form";
import { CurrencyCode } from "@shopify/react-i18n";
import {
  Form,
  useActionData,
  useNavigation,
  useSubmit,
} from "@remix-run/react";
import {
  ActiveDatesCard,
  CombinationCard,
  DiscountClass,
  DiscountMethod,
  MethodCard,
  DiscountStatus,
  RequirementType,
  SummaryCard,
  UsageLimitsCard,
} from "@shopify/discount-app-components";
import {
  Banner,
  Card,
  Text,
  Layout,
  Page,
  PageActions,
  TextField,
  BlockStack,
  Box,
} from "@shopify/polaris";

import shopify from "../shopify.server";

export const action = async ({ params, request }) => {
  const { functionId } = params;
  const { admin } = await shopify.authenticate.admin(request);
  const formData = await request.formData();
  const {
    title,
    method,
    code,
    combinesWith,
    usageLimit,
    appliesOncePerCustomer,
    startsAt,
    endsAt,
    configuration,
  } = JSON.parse(formData.get("discount"));

  const baseDiscount = {
    functionId,
    title,
    combinesWith,
    startsAt: new Date(startsAt),
    endsAt: endsAt && new Date(endsAt),
  };

  const metafields = [
    {
      namespace: "$app:2FORDEALC",
      key: "function-configuration",
      type: "json",
      value: JSON.stringify({
        fixedAmountOff: configuration.fixedAmountOff,
        tags: configuration.tags.split(",").map(tag => tag.trim()), 

      }),
    },
  ];

  if (method === DiscountMethod.Code) {
    const baseCodeDiscount = {
      ...baseDiscount,
      title: code,
      code,
      usageLimit,
      appliesOncePerCustomer,
    };

    const response = await admin.graphql(
      `#graphql
          mutation CreateCodeDiscount($discount: DiscountCodeAppInput!) {
            discountCreate: discountCodeAppCreate(codeAppDiscount: $discount) {
              codeAppDiscount{
                discountId
              }
              userErrors {
                code
                message
                field
              }
            }
          }`,
      {
        variables: {
          discount: {
            ...baseCodeDiscount,
            metafields,
          },
        },
      },
    );

    const responseJson = await response.json();

    const errors = responseJson.data.discountCreate?.userErrors;
    const discount = responseJson.data.discountCreate?.codeAppDiscount;
    return json({ errors, discount: { ...discount, functionId } });
  } else {
    const response = await admin.graphql(
      `#graphql
          mutation CreateAutomaticDiscount($discount: DiscountAutomaticAppInput!) {
            discountCreate: discountAutomaticAppCreate(automaticAppDiscount: $discount) {
              automaticAppDiscount {
                discountId
              }
              userErrors {
                code
                message
                field
              }
            }
          }`,
      {
        variables: {
          discount: {
            ...baseDiscount,
            metafields,
          },
        },
      },
    );

    const responseJson = await response.json();
    const errors = responseJson.data.discountCreate?.userErrors;
    return json({ errors });
  }
};

export default function VolumeNew() {
  const submitForm = useSubmit();
  const actionData = useActionData();
  const navigation = useNavigation();
  const todaysDate = useMemo(() => new Date(), []);

  const isLoading = navigation.state === "submitting";
  const currencyCode = CurrencyCode.Cad;
  const submitErrors = actionData?.errors || [];
  const returnToDiscounts = () => open("shopify://admin/discounts", "_top");

  useEffect(() => {
    if (actionData?.errors.length === 0 && actionData?.discount) {
      returnToDiscounts();
    }
  }, [actionData]);

  const {
    fields: {
      discountTitle,
      discountCode,
      discountMethod,
      combinesWith,
      requirementType,
      requirementSubtotal,
      requirementfixedAmountOff,
      usageLimit,
      appliesOncePerCustomer,
      startDate,
      endDate,
      configuration,
    },
    submit,
  } = useForm({
    fields: {
      discountTitle: useField(""),
      discountMethod: useField(DiscountMethod.Code),
      discountCode: useField(""),
      combinesWith: useField({
        orderDiscounts: false,
        productDiscounts: false,
        shippingDiscounts: false,
      }),
      requirementType: useField(RequirementType.None),
      requirementSubtotal: useField("0"),
      requirementfixedAmountOff: useField("0"),
      usageLimit: useField(null),
      appliesOncePerCustomer: useField(false),
      startDate: useField(todaysDate),
      endDate: useField(null),
      configuration: {
        fixedAmountOff: useField("1"),
        tags: useField(""),
      },
    },
    onSubmit: async (form) => {
      const discount = {
        title: form.discountTitle,
        method: form.discountMethod,
        code: form.discountCode,
        combinesWith: form.combinesWith,
        usageLimit: form.usageLimit == null ? null : parseInt(form.usageLimit),
        appliesOncePerCustomer: form.appliesOncePerCustomer,
        startsAt: form.startDate,
        endsAt: form.endDate,
        configuration: {
          fixedAmountOff: parseInt(form.configuration.fixedAmountOff),
        },
      };

      submitForm({ discount: JSON.stringify(discount) }, { method: "post" });

      return { status: "success" };
    },
  });

  const errorBanner =
    submitErrors.length > 0 ? (
      <Layout.Section>
        <Banner tone="critical">
          <p>There were some issues with your form submission:</p>
          <ul>
            {submitErrors.map(({ message, field }, index) => {
              return (
                <li key={`${message}${index}`}>
                  {field.join(".")} {message}
                </li>
              );
            })}
          </ul>
        </Banner>
      </Layout.Section>
    ) : null;

  return (
    <Page>
      <ui-title-bar title="2 For Discount">
        <button variant="breadcrumb" onClick={returnToDiscounts}>
          Discounts
        </button>
        <button variant="primary" onClick={submit}>
          Save discount
        </button>
      </ui-title-bar>
      <Layout>
        {errorBanner}
        <Layout.Section>
          <Form method="post">
            <BlockStack align="space-around" gap="200">
              <MethodCard
                title="2 For Deal C"
                discountTitle={discountTitle}
                discountClass={DiscountClass.Product}
                discountCode={discountCode}
                discountMethod={discountMethod}
              />
              <Box paddingBlockEnd="300">
                <Card>
                  <BlockStack>
                    <Text variant="headingMd" as="h2">
                      Amount Off SKU (Rands)
                    </Text>
                    <TextField
                      label="Discount per item (1)"
                      autoComplete="on"
                      {...configuration.fixedAmountOff}
                    />
                  </BlockStack>
                </Card>
              </Box>
              <Box paddingBlockEnd="300">
  <Card>
    <BlockStack>
      <Text variant="headingMd" as="h2">
        Tags for Discount (e.g., "2FORDEALA")
      </Text>
      <TextField
        label="Tags"
        placeholder="Enter tags separated by commas"
        value={tags}
        autoComplete="on"
        {...configuration.tags}
      />
    </BlockStack>
  </Card>
</Box>
              {discountMethod.value === DiscountMethod.Code && (
                <UsageLimitsCard
                  totalUsageLimit={usageLimit}
                  oncePerCustomer={appliesOncePerCustomer}
                />
              )}
              <CombinationCard
                combinableDiscountTypes={combinesWith}
                discountClass={DiscountClass.Product}
                discountDescriptor={"Discount"}
              />
              <ActiveDatesCard
                startDate={startDate}
                endDate={endDate}
                timezoneAbbreviation="EST"
              />
            </BlockStack>
          </Form>
        </Layout.Section>
        <Layout.Section variant="oneThird">
          <SummaryCard
            header={{
              discountMethod: discountMethod.value,
              discountDescriptor:
                discountMethod.value === DiscountMethod.Automatic
                  ? discountTitle.value
                  : discountCode.value,
              appDiscountType: "Volume",
              isEditing: false,
            }}
            performance={{
              status: DiscountStatus.Scheduled,
              usageCount: 0,
              isEditing: false,
            }}
            minimumRequirements={{
              requirementType: requirementType.value,
              subtotal: requirementSubtotal.value,
              fixedAmountOff: requirementfixedAmountOff.value,
              currencyCode: currencyCode,
            }}
            usageLimits={{
              oncePerCustomer: appliesOncePerCustomer.value,
              totalUsageLimit: usageLimit.value,
            }}
            activeDates={{
              startDate: startDate.value,
              endDate: endDate.value,
            }}
          />
        </Layout.Section>
        <Layout.Section>
          <PageActions
            primaryAction={{
              content: "Save discount",
              onAction: submit,
              loading: isLoading,
            }}
            secondaryActions={[
              {
                content: "Discard",
                onAction: returnToDiscounts,
              },
            ]}
          />
        </Layout.Section>
      </Layout>
    </Page>
  );
}

 #functions #shopify

Replies 0 (0)