fix: Use region name in product pricing exports (#8373)

* fix:Bug fixes to product import

* fix:Add an export failed notification if an export fails

* fix: Use region name in product export prices
This commit is contained in:
Stevche Radevski
2024-07-31 15:34:27 +02:00
committed by GitHub
parent 12c6a1a022
commit 31449972ed
8 changed files with 157 additions and 22 deletions
@@ -1,10 +1,19 @@
import { BigNumberInput, HttpTypes, PricingTypes } from "@medusajs/types"
import { upperCaseFirst } from "@medusajs/utils"
import {
RegionTypes,
BigNumberInput,
HttpTypes,
PricingTypes,
} from "@medusajs/types"
import { MedusaError, upperCaseFirst } from "@medusajs/utils"
// We want to have one row per variant, so we need to normalize the data
export const normalizeForExport = (
product: HttpTypes.AdminProduct[]
product: HttpTypes.AdminProduct[],
{ regions }: { regions: RegionTypes.RegionDTO[] }
): object[] => {
// Currently region names are treated as case-insensitive.
const regionsMap = new Map(regions.map((r) => [r.id, r]))
const res = product.reduce((acc: object[], product) => {
const variants = product.variants ?? []
if (!variants.length) {
@@ -15,7 +24,7 @@ export const normalizeForExport = (
variants.forEach((v) => {
const toPush = {
...normalizeProductForExport(product),
...normalizeVariantForExport(v),
...normalizeVariantForExport(v, regionsMap),
} as any
delete toPush["Product Variants"]
@@ -81,16 +90,31 @@ const normalizeProductForExport = (product: HttpTypes.AdminProduct): object => {
const normalizeVariantForExport = (
variant: HttpTypes.AdminProductVariant & {
price_set?: PricingTypes.PriceSetDTO
}
},
regionsMap: Map<string, RegionTypes.RegionDTO>
): object => {
const flattenedPrices = variant.price_set?.prices
?.sort((a, b) => b.currency_code!.localeCompare(a.currency_code!))
.reduce((acc: Record<string, BigNumberInput>, price) => {
const regionRule = price.price_rules?.find(
(r) => r.attribute === "region"
(r) => r.attribute === "region_id"
)
if (regionRule) {
acc[beautifyKey(`variant_price_${regionRule.value}`)] = price.amount!
const region = regionsMap.get(regionRule?.value!)
if (!region) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Region with id ${regionRule?.value} not found`
)
}
const regionKey = `variant_price_${region.name
.toLowerCase()
.split(" ")
.join("_")}_[${region.currency_code.toUpperCase()}]`
acc[beautifyKey(regionKey)] = price.amount!
} else if (!price.price_rules?.length) {
acc[
beautifyKey(`variant_price_${price.currency_code!.toUpperCase()}`)
@@ -70,6 +70,7 @@ const variantFieldsToOmit = new Map([["variant_product_id", true]])
// These fields can have a numeric value, but they are stored as string in the DB so we need to normalize them
const stringFields = [
"product_tag_",
"variant_option_",
"variant_barcode",
"variant_sku",
"variant_ean",
@@ -78,6 +79,12 @@ const stringFields = [
"variant_mid_code",
]
const booleanFields = [
"product_discountable",
"variant_manage_inventory",
"variant_allow_backorder",
]
const normalizeProductForImport = (
rawProduct: object
): HttpTypes.AdminCreateProduct => {
@@ -221,9 +228,20 @@ const normalizeVariantForImport = (
}
const getNormalizedValue = (key: string, value: any): any => {
return stringFields.some((field) => key.startsWith(field))
let res = stringFields.some((field) => key.startsWith(field))
? value?.toString()
: value
if (booleanFields.some((field) => key.startsWith(field))) {
if (value === "TRUE") {
res = true
}
if (value === "FALSE") {
res = false
}
}
return res
}
const snakecaseKey = (key: string): string => {
@@ -1,4 +1,8 @@
import { IFileModuleService, HttpTypes } from "@medusajs/types"
import {
IFileModuleService,
HttpTypes,
IRegionModuleService,
} from "@medusajs/types"
import { ModuleRegistrationName, convertJsonToCsv } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
import { normalizeForExport } from "../helpers/normalize-for-export"
@@ -7,7 +11,16 @@ export const generateProductCsvStepId = "generate-product-csv"
export const generateProductCsvStep = createStep(
generateProductCsvStepId,
async (products: HttpTypes.AdminProduct[], { container }) => {
const normalizedData = normalizeForExport(products)
const regionService = container.resolve<IRegionModuleService>(
ModuleRegistrationName.REGION
)
const regions = await regionService.listRegions(
{},
{ select: ["id", "name", "currency_code"], take: null }
)
const normalizedData = normalizeForExport(products, { regions })
const csvContent = convertJsonToCsv(normalizedData)
const fileModule: IFileModuleService = container.resolve(
@@ -6,7 +6,7 @@ import {
import { WorkflowTypes } from "@medusajs/types"
import { generateProductCsvStep, getAllProductsStep } from "../steps"
import { useRemoteQueryStep } from "../../common"
import { sendNotificationsStep } from "../../notification"
import { notifyOnFailureStep, sendNotificationsStep } from "../../notification"
export const exportProductsWorkflowId = "export-products"
export const exportProductsWorkflow = createWorkflow(
@@ -19,6 +19,22 @@ export const exportProductsWorkflow = createWorkflow(
backgroundExecution: true,
})
const failureNotification = transform({ input }, (data) => {
return [
{
// We don't need the recipient here for now, but if we want to push feed notifications to a specific user we could add it.
to: "",
channel: "feed",
template: "admin-ui",
data: {
title: "Product export",
description: `Failed to export products, please try again later.`,
},
},
]
})
notifyOnFailureStep(failureNotification)
const file = generateProductCsvStep(products)
const fileDetails = useRemoteQueryStep({
fields: ["id", "url"],