Merge branch 'develop' into fix/2152

This commit is contained in:
Harminder Virk
2024-05-17 09:54:37 +05:30
62 changed files with 1838 additions and 1176 deletions
-1
View File
@@ -28,7 +28,6 @@
"@medusajs/utils": "workspace:^", "@medusajs/utils": "workspace:^",
"@medusajs/workflow-engine-inmemory": "workspace:*", "@medusajs/workflow-engine-inmemory": "workspace:*",
"faker": "^5.5.3", "faker": "^5.5.3",
"medusa-interfaces": "workspace:*",
"pg": "^8.11.0", "pg": "^8.11.0",
"typeorm": "^0.3.16" "typeorm": "^0.3.16"
}, },
@@ -0,0 +1,473 @@
import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk"
import {
FulfillmentWorkflow,
IOrderModuleService,
IRegionModuleService,
IStockLocationServiceNext,
OrderWorkflow,
ProductDTO,
RegionDTO,
ShippingOptionDTO,
StockLocationDTO,
} from "@medusajs/types"
import { medusaIntegrationTestRunner } from "medusa-test-utils/dist"
import {
createReturnOrderWorkflow,
createShippingOptionsWorkflow,
} from "@medusajs/core-flows"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
RuleOperator,
} from "@medusajs/utils"
jest.setTimeout(500000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
const providerId = "manual_test-provider"
async function prepareDataFixtures({ container }) {
const fulfillmentService = container.resolve(
ModuleRegistrationName.FULFILLMENT
)
const salesChannelService = container.resolve(
ModuleRegistrationName.SALES_CHANNEL
)
const stockLocationModule: IStockLocationServiceNext = container.resolve(
ModuleRegistrationName.STOCK_LOCATION
)
const productModule = container.resolve(ModuleRegistrationName.PRODUCT)
const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY)
const shippingProfile = await fulfillmentService.createShippingProfiles({
name: "test",
type: "default",
})
const fulfillmentSet = await fulfillmentService.create({
name: "Test fulfillment set",
type: "manual_test",
})
const serviceZone = await fulfillmentService.createServiceZones({
name: "Test service zone",
fulfillment_set_id: fulfillmentSet.id,
geo_zones: [
{
type: "country",
country_code: "US",
},
],
})
const regionService = container.resolve(
ModuleRegistrationName.REGION
) as IRegionModuleService
const [region] = await regionService.create([
{
name: "Test region",
currency_code: "eur",
countries: ["fr"],
},
])
const salesChannel = await salesChannelService.create({
name: "Webshop",
})
const location: StockLocationDTO = await stockLocationModule.create({
name: "Warehouse",
address: {
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
})
const [product] = await productModule.create([
{
title: "Test product",
variants: [
{
title: "Test variant",
sku: "test-variant",
},
],
},
])
const inventoryItem = await inventoryModule.create({
sku: "inv-1234",
})
await inventoryModule.createInventoryLevels([
{
inventory_item_id: inventoryItem.id,
location_id: location.id,
stocked_quantity: 2,
reserved_quantity: 0,
},
])
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
await remoteLink.create([
{
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
[Modules.FULFILLMENT]: {
fulfillment_set_id: fulfillmentSet.id,
},
},
{
[Modules.SALES_CHANNEL]: {
sales_channel_id: salesChannel.id,
},
[Modules.STOCK_LOCATION]: {
stock_location_id: location.id,
},
},
{
[Modules.PRODUCT]: {
variant_id: product.variants[0].id,
},
[Modules.INVENTORY]: {
inventory_item_id: inventoryItem.id,
},
},
])
const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput =
{
name: "Return shipping option",
price_type: "flat",
service_zone_id: serviceZone.id,
shipping_profile_id: shippingProfile.id,
provider_id: providerId,
type: {
code: "manual-type",
label: "Manual Type",
description: "Manual Type Description",
},
prices: [
{
currency_code: "usd",
amount: 10,
},
{
region_id: region.id,
amount: 100,
},
],
rules: [
{
attribute: "is_return",
operator: RuleOperator.EQ,
value: '"true"',
},
],
}
const { result } = await createShippingOptionsWorkflow(container).run({
input: [shippingOptionData],
})
const remoteQueryObject = remoteQueryObjectFromString({
entryPoint: "shipping_option",
variables: {
id: result[0].id,
},
fields: [
"id",
"name",
"price_type",
"service_zone_id",
"shipping_profile_id",
"provider_id",
"data",
"metadata",
"type.*",
"created_at",
"updated_at",
"deleted_at",
"shipping_option_type_id",
"prices.*",
],
})
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const [createdShippingOption] = await remoteQuery(remoteQueryObject)
return {
shippingOption: createdShippingOption,
region,
salesChannel,
location,
product,
}
}
async function createOrderFixture({ container, product }) {
const orderService: IOrderModuleService = container.resolve(
ModuleRegistrationName.ORDER
)
let order = await orderService.create({
region_id: "test_region_idclear",
email: "foo@bar.com",
items: [
{
title: "Custom Item 2",
variant_sku: product.variants[0].sku,
variant_title: product.variants[0].title,
quantity: 1,
unit_price: 50,
adjustments: [
{
code: "VIP_25 ETH",
amount: "0.000000000000000005",
description: "VIP discount",
promotion_id: "prom_123",
provider_id: "coupon_kings",
},
],
} as any,
],
transactions: [
{
amount: 50, // TODO: check calculation, I think it should be 60 wit the shipping but the order total is 50
currency_code: "usd",
},
],
sales_channel_id: "test",
shipping_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
phone: "12345",
},
billing_address: {
first_name: "Test",
last_name: "Test",
address_1: "Test",
city: "Test",
country_code: "US",
postal_code: "12345",
},
shipping_methods: [
{
name: "Test shipping method",
amount: 10,
data: {},
tax_lines: [
{
description: "shipping Tax 1",
tax_rate_id: "tax_usa_shipping",
code: "code",
rate: 10,
},
],
adjustments: [
{
code: "VIP_10",
amount: 1,
description: "VIP discount",
promotion_id: "prom_123",
},
],
},
],
currency_code: "usd",
customer_id: "joe",
})
await orderService.addOrderAction([
{
action: "FULFILL_ITEM",
order_id: order.id,
version: order.version,
reference: "fullfilment",
reference_id: "fulfill_123",
details: {
reference_id: order.items![0].id,
quantity: 1,
},
},
{
action: "SHIP_ITEM",
order_id: order.id,
version: order.version,
reference: "fullfilment",
reference_id: "fulfill_123",
details: {
reference_id: order.items![0].id,
quantity: 1,
},
},
])
await orderService.applyPendingOrderActions(order.id)
order = await orderService.retrieve(order.id, {
relations: ["items"],
})
return order
}
medusaIntegrationTestRunner({
env,
testSuite: ({ getContainer }) => {
let container
beforeAll(() => {
container = getContainer()
})
describe("Create return order workflow", () => {
let shippingOption: ShippingOptionDTO
let region: RegionDTO
let location: StockLocationDTO
let product: ProductDTO
let orderService: IOrderModuleService
beforeEach(async () => {
const fixtures = await prepareDataFixtures({
container,
})
shippingOption = fixtures.shippingOption
region = fixtures.region
location = fixtures.location
product = fixtures.product
orderService = container.resolve(ModuleRegistrationName.ORDER)
})
it("should create a return order", async () => {
const order = await createOrderFixture({ container, product })
const createReturnOrderData: OrderWorkflow.CreateOrderReturnWorkflowInput =
{
order_id: order.id,
return_shipping: {
option_id: shippingOption.id,
},
items: [
{
id: order.items![0].id,
quantity: 1,
},
],
}
await createReturnOrderWorkflow(container).run({
input: createReturnOrderData,
throwOnError: false,
})
const remoteQuery = container.resolve(
ContainerRegistrationKeys.REMOTE_QUERY
)
const remoteQueryObject = remoteQueryObjectFromString({
entryPoint: "order",
variables: {
id: order.id,
},
fields: [
"*",
"items.*",
"shipping_methods.*",
"total",
"item_total",
"fulfillments.*",
],
})
const [returnOrder] = await remoteQuery(remoteQueryObject)
expect(returnOrder).toEqual(
expect.objectContaining({
id: expect.any(String),
display_id: 1,
region_id: "test_region_idclear",
customer_id: "joe",
version: 2,
sales_channel_id: "test", // TODO: What about order with a sales channel but a shipping option link to a stock from another channel?
status: "pending",
is_draft_order: false,
email: "foo@bar.com",
currency_code: "usd",
shipping_address_id: expect.any(String),
billing_address_id: expect.any(String),
items: [
expect.objectContaining({
id: order.items![0].id,
title: "Custom Item 2",
variant_sku: product.variants[0].sku,
variant_title: product.variants[0].title,
requires_shipping: true,
is_discountable: true,
is_tax_inclusive: false,
compare_at_unit_price: null,
unit_price: 50,
quantity: 1,
detail: expect.objectContaining({
id: expect.any(String),
order_id: expect.any(String),
version: 2,
item_id: expect.any(String),
quantity: 1,
fulfilled_quantity: 1,
shipped_quantity: 1,
return_requested_quantity: 1,
return_received_quantity: 0,
return_dismissed_quantity: 0,
written_off_quantity: 0,
}),
}),
],
shipping_methods: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
name: "Test shipping method",
description: null,
is_tax_inclusive: false,
shipping_option_id: null,
amount: 10,
order_id: expect.any(String),
}),
expect.objectContaining({
id: expect.any(String),
name: shippingOption.name,
description: null,
is_tax_inclusive: false,
shipping_option_id: shippingOption.id,
amount: 10,
order_id: expect.any(String),
}),
]),
fulfillments: [
expect.objectContaining({
id: expect.any(String),
location_id: location.id,
provider_id: providerId,
shipping_option_id: shippingOption.id,
// TODO: Validate the address once we are fixed on it
/*delivery_address: {
id: "fuladdr_01HY0RTAP0P1EEAFK7BXJ0BKBN",
},*/
}),
],
})
)
})
})
},
})
-1
View File
@@ -31,7 +31,6 @@
"@medusajs/utils": "workspace:^", "@medusajs/utils": "workspace:^",
"@medusajs/workflow-engine-inmemory": "workspace:*", "@medusajs/workflow-engine-inmemory": "workspace:*",
"faker": "^5.5.3", "faker": "^5.5.3",
"medusa-interfaces": "workspace:*",
"medusa-test-utils": "workspace:*", "medusa-test-utils": "workspace:*",
"pg": "^8.11.0", "pg": "^8.11.0",
"typeorm": "^0.3.16" "typeorm": "^0.3.16"
@@ -62,6 +62,7 @@
}, },
"actions": { "actions": {
"save": "Save", "save": "Save",
"select": "Select",
"saveAsDraft": "Save as draft", "saveAsDraft": "Save as draft",
"publish": "Publish", "publish": "Publish",
"create": "Create", "create": "Create",
@@ -653,8 +654,8 @@
} }
}, },
"shipping": { "shipping": {
"title": "Location & Shipping", "title": "Locations & Shipping",
"domain": "Location & Shipping", "domain": "Locations & Shipping",
"description": "Choose where you ship and how much you charge for shipping at checkout. Define shipping options specific for your locations.", "description": "Choose where you ship and how much you charge for shipping at checkout. Define shipping options specific for your locations.",
"createLocation": "Create location", "createLocation": "Create location",
"createLocationDetailsHint": "Specify the details of the location.", "createLocationDetailsHint": "Specify the details of the location.",
@@ -714,10 +715,12 @@
"edit": { "edit": {
"title": "Edit Service Zone" "title": "Edit Service Zone"
}, },
"editAreasTitle": "Manage {{zone}} areas",
"deleteWarning": "Are you sure you want to delete \"{{name}}\". This will also delete all assocciated shipping options.", "deleteWarning": "Are you sure you want to delete \"{{name}}\". This will also delete all assocciated shipping options.",
"toast": { "toast": {
"delete": "Zone \"{{name}}\" deleted successfully." "delete": "Zone \"{{name}}\" deleted successfully."
}, },
"manageAreas": "Manage areas",
"editPrices": "Edit prices", "editPrices": "Edit prices",
"editOption": "Edit option", "editOption": "Edit option",
"optionsLength_one": "shipping option", "optionsLength_one": "shipping option",
@@ -745,8 +748,8 @@
"allocation": "Shipping amount", "allocation": "Shipping amount",
"fixed": "Fixed", "fixed": "Fixed",
"fixedDescription": "Shipping option's price is always the same amount.", "fixedDescription": "Shipping option's price is always the same amount.",
"enable": "Enable in store", "enable": "Show publicly",
"enableDescription": "Enable or disable the shipping option visiblity in store", "enableDescription": "When disabled, the shipping option can only be applied by admins.",
"calculated": "Calculated", "calculated": "Calculated",
"calculatedDescription": "Shipping option's price is calculated by the fulfillment provider.", "calculatedDescription": "Shipping option's price is calculated by the fulfillment provider.",
"profile": "Shipping profile" "profile": "Shipping profile"
@@ -41,7 +41,7 @@ export const useStockLocation = (
) => { ) => {
const { data, ...rest } = useQuery({ const { data, ...rest } = useQuery({
queryFn: () => client.stockLocations.retrieve(id, query), queryFn: () => client.stockLocations.retrieve(id, query),
queryKey: stockLocationsQueryKeys.detail(id, query), queryKey: stockLocationsQueryKeys.details(),
...options, ...options,
}) })
@@ -744,6 +744,13 @@ export const RouteMap: RouteObject[] = [
"../../v2-routes/shipping/service-zone-edit" "../../v2-routes/shipping/service-zone-edit"
), ),
}, },
{
path: "edit-areas",
lazy: () =>
import(
"../../v2-routes/shipping/service-zone-areas-edit"
),
},
{ {
path: "shipping-option", path: "shipping-option",
children: [ children: [
@@ -764,6 +771,13 @@ export const RouteMap: RouteObject[] = [
"../../v2-routes/shipping/shipping-option-edit" "../../v2-routes/shipping/shipping-option-edit"
), ),
}, },
{
path: "edit-pricing",
lazy: () =>
import(
"../../v2-routes/shipping/shipping-options-edit-pricing"
),
},
], ],
}, },
], ],
@@ -156,7 +156,7 @@ function ShippingOption({
{ {
label: t("shipping.serviceZone.editPrices"), label: t("shipping.serviceZone.editPrices"),
icon: <CurrencyDollar />, icon: <CurrencyDollar />,
disabled: true, to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${option.service_zone_id}/shipping-option/${option.id}/edit-pricing`,
}, },
{ {
label: t("actions.delete"), label: t("actions.delete"),
@@ -199,7 +199,7 @@ function ServiceZoneOptions({
{t("shipping.serviceZone.shippingOptions")} {t("shipping.serviceZone.shippingOptions")}
</span> </span>
<Button <Button
className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent" className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent active:bg-transparent"
variant="transparent" variant="transparent"
onClick={() => onClick={() =>
navigate( navigate(
@@ -231,7 +231,7 @@ function ServiceZoneOptions({
{t("shipping.serviceZone.returnOptions")} {t("shipping.serviceZone.returnOptions")}
</span> </span>
<Button <Button
className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent" className="text-ui-fg-interactive txt-small px-0 font-medium hover:bg-transparent active:bg-transparent"
variant="transparent" variant="transparent"
onClick={() => onClick={() =>
navigate( navigate(
@@ -313,6 +313,7 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
.filter((g) => g.type === "country") .filter((g) => g.type === "country")
.map((g) => g.country_code) .map((g) => g.country_code)
.map((code) => staticCountries.find((c) => c.iso_2 === code)) .map((code) => staticCountries.find((c) => c.iso_2 === code))
.sort((c1, c2) => c1.name.localeCompare(c2.name))
}, zone.geo_zones) }, zone.geo_zones)
const [shippingOptionsCount, returnOptionsCount] = useMemo(() => { const [shippingOptionsCount, returnOptionsCount] = useMemo(() => {
@@ -385,16 +386,16 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
groups={[ groups={[
{ {
actions: [ actions: [
// {
// label: t("shipping.serviceZone.addOption"),
// icon: <Plus />,
// to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/shipping-option/create`,
// },
{ {
label: t("actions.edit"), label: t("actions.edit"),
icon: <PencilSquare />, icon: <PencilSquare />,
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit`, to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit`,
}, },
{
label: t("shipping.serviceZone.manageAreas"),
icon: <Map />,
to: `/settings/shipping/${locationId}/fulfillment-set/${fulfillmentSetId}/service-zone/${zone.id}/edit-areas`,
},
{ {
label: t("actions.delete"), label: t("actions.delete"),
icon: <Trash />, icon: <Trash />,
@@ -181,7 +181,7 @@ function Location(props: LocationProps) {
]} ]}
/> />
<Button <Button
className="text-ui-fg-interactive rounded-none pl-5 hover:bg-transparent" className="text-ui-fg-interactive rounded-none pl-5 hover:bg-transparent active:bg-transparent"
onClick={() => navigate(`/settings/shipping/${location.id}`)} onClick={() => navigate(`/settings/shipping/${location.id}`)}
variant="transparent" variant="transparent"
> >
@@ -0,0 +1,344 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import {
ColumnDef,
createColumnHelper,
RowSelectionState,
} from "@tanstack/react-table"
import * as zod from "zod"
import {
Alert,
Badge,
Button,
Checkbox,
Heading,
IconButton,
Text,
toast,
} from "@medusajs/ui"
import { RegionCountryDTO, RegionDTO, ServiceZoneDTO } from "@medusajs/types"
import { useTranslation } from "react-i18next"
import { XMarkMini } from "@medusajs/icons"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { SplitView } from "../../../../../components/layout/split-view"
import {
useCreateServiceZone,
useUpdateServiceZone,
} from "../../../../../hooks/api/stock-locations"
import { useEffect, useMemo, useState } from "react"
import { useCountryTableQuery } from "../../../../regions/common/hooks/use-country-table-query"
import { useCountries } from "../../../../regions/common/hooks/use-countries"
import { countries as staticCountries } from "../../../../../lib/countries"
import { useDataTable } from "../../../../../hooks/use-data-table"
import { useCountryTableColumns } from "../../../../regions/common/hooks/use-country-table-columns"
import { DataTable } from "../../../../../components/table/data-table"
const PREFIX = "ac"
const PAGE_SIZE = 50
const ConditionsFooter = ({ onSave }: { onSave: () => void }) => {
const { t } = useTranslation()
return (
<div className="flex items-center justify-end gap-x-2 border-t p-4">
<SplitView.Close type="button" asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</SplitView.Close>
<Button size="small" type="button" onClick={onSave}>
{t("actions.select")}
</Button>
</div>
)
}
const EditeServiceZoneSchema = zod.object({
countries: zod.array(zod.string().length(2)).min(1),
})
type EditServiceZoneAreasFormProps = {
fulfillmentSetId: string
locationId: string
zone: ServiceZoneDTO
}
export function EditServiceZoneAreasForm({
fulfillmentSetId,
locationId,
zone,
}: EditServiceZoneAreasFormProps) {
const { t } = useTranslation()
const { handleSuccess } = useRouteModal()
const [open, setOpen] = useState(false)
const [rowSelection, setRowSelection] = useState<RowSelectionState>(
zone.geo_zones
.map((z) => z.country_code)
.reduce((acc, v) => {
acc[v] = true
return acc
}, {})
)
const form = useForm<zod.infer<typeof EditeServiceZoneSchema>>({
defaultValues: {
countries: zone.geo_zones.map((z) => z.country_code),
},
resolver: zodResolver(EditeServiceZoneSchema),
})
const { mutateAsync: editServiceZone, isPending: isLoading } =
useUpdateServiceZone(fulfillmentSetId, zone.id, locationId)
const handleSubmit = form.handleSubmit(async (data) => {
try {
await editServiceZone({
geo_zones: data.countries.map((iso2) => ({
country_code: iso2,
type: "country",
})),
})
} catch (e) {
toast.error(t("general.error"), {
description: e.message,
dismissLabel: t("general.close"),
})
}
handleSuccess()
})
const handleOpenChange = (open: boolean) => {
setOpen(open)
}
const { searchParams, raw } = useCountryTableQuery({
pageSize: PAGE_SIZE,
prefix: PREFIX,
})
const { countries, count } = useCountries({
countries: staticCountries.map((c, i) => ({
display_name: c.display_name,
name: c.name,
id: i as any,
iso_2: c.iso_2,
iso_3: c.iso_3,
num_code: c.num_code,
region_id: null,
region: {} as RegionDTO,
})),
...searchParams,
})
const columns = useColumns()
const { table } = useDataTable({
data: countries || [],
columns,
count,
enablePagination: true,
enableRowSelection: true,
getRowId: (row) => row.iso_2,
pageSize: PAGE_SIZE,
rowSelection: {
state: rowSelection,
updater: setRowSelection,
},
prefix: PREFIX,
})
const countriesWatch = form.watch("countries")
const onCountriesSave = () => {
form.setValue("countries", Object.keys(rowSelection))
setOpen(false)
}
const removeCountry = (iso2: string) => {
const state = { ...rowSelection }
delete state[iso2]
setRowSelection(state)
form.setValue(
"countries",
countriesWatch.filter((c) => c !== iso2)
)
}
const clearAll = () => {
setRowSelection({})
form.setValue("countries", [])
}
const selectedCountries = useMemo(() => {
return staticCountries.filter((c) => c.iso_2 in rowSelection)
}, [countriesWatch])
useEffect(() => {
// set selected rows from form state on open
if (open) {
setRowSelection(
countriesWatch.reduce((acc, c) => {
acc[c] = true
return acc
}, {})
)
}
}, [open])
const showAreasError =
form.formState.errors["countries"]?.type === "too_small"
return (
<RouteFocusModal.Form form={form}>
<form
className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit}
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isLoading}>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="m-auto flex h-full w-full flex-col items-center divide-y overflow-hidden">
<SplitView open={open} onOpenChange={handleOpenChange}>
<SplitView.Content className="mx-auto max-w-[720px]">
<div className="container w-fit px-1 py-8">
<Heading className="mt-8 text-2xl">
{t("shipping.serviceZone.editAreasTitle", {
zone: zone.name,
})}
</Heading>
</div>
<div className="container flex items-center justify-between py-8 pr-1">
<div>
<Text weight="plus">
{t("shipping.serviceZone.areas.title")}
</Text>
<Text className="text-ui-fg-subtle mt-2">
{t("shipping.serviceZone.areas.description")}
</Text>
</div>
<Button
onClick={() => setOpen(true)}
variant="secondary"
type="button"
>
{t("shipping.serviceZone.areas.manage")}
</Button>
</div>
{!!selectedCountries.length && (
<div className="flex flex-wrap items-center gap-4">
{selectedCountries.map((c) => (
<Badge
key={c.iso_2}
className="text-ui-fg-subtle txt-small flex items-center gap-1 divide-x pr-0"
>
{c.display_name}
<IconButton
type="button"
onClick={() => removeCountry(c.iso_2)}
className="text-ui-fg-subtle p-0 px-1 pt-[1px]"
variant="transparent"
>
<XMarkMini />
</IconButton>
</Badge>
))}
<Button
type="button"
onClick={clearAll}
variant="transparent"
className="txt-small text-ui-fg-muted font-medium"
>
{t("actions.clearAll")}
</Button>
</div>
)}
{showAreasError && (
<Alert dismissible variant="error">
{t("shipping.serviceZone.areas.error")}
</Alert>
)}
</SplitView.Content>
<SplitView.Drawer>
<div className="flex size-full flex-col overflow-hidden">
<DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
count={count}
search
pagination
layout="fill"
orderBy={["name", "code"]}
queryObject={raw}
prefix={PREFIX}
/>
<ConditionsFooter onSave={onCountriesSave} />
</div>
</SplitView.Drawer>
</SplitView>
</RouteFocusModal.Body>
</form>
</RouteFocusModal.Form>
)
}
const columnHelper = createColumnHelper<RegionCountryDTO>()
const useColumns = () => {
const base = useCountryTableColumns()
return useMemo(
() => [
columnHelper.display({
id: "select",
header: ({ table }) => {
return (
<Checkbox
checked={
table.getIsSomePageRowsSelected()
? "indeterminate"
: table.getIsAllPageRowsSelected()
}
onCheckedChange={(value) =>
table.toggleAllPageRowsSelected(!!value)
}
/>
)
},
cell: ({ row }) => {
const isPreselected = !row.getCanSelect()
return (
<Checkbox
checked={row.getIsSelected() || isPreselected}
disabled={isPreselected}
onCheckedChange={(value) => row.toggleSelected(!!value)}
onClick={(e) => {
e.stopPropagation()
}}
/>
)
},
}),
...base,
],
[base]
) as ColumnDef<RegionCountryDTO>[]
}
@@ -0,0 +1 @@
export * from "./edit-service-zone-areas-form"
@@ -0,0 +1 @@
export { ServiceZoneAreasEdit as Component } from "./service-zone-areas-edit"
@@ -0,0 +1,45 @@
import { json, useParams } from "react-router-dom"
import { RouteFocusModal } from "../../../components/route-modal"
import { EditServiceZoneAreasForm } from "./components/edit-region-areas-form"
import { useStockLocation } from "../../../hooks/api/stock-locations"
export const ServiceZoneAreasEdit = () => {
const { location_id, fset_id, zone_id } = useParams()
const { stock_location, isPending, isError, error } = useStockLocation(
location_id!,
{
// NOTE: use same query for all details page subroutes & fetches
fields:
"name,*sales_channels,address.city,address.country_code,fulfillment_sets.type,fulfillment_sets.name,*fulfillment_sets.service_zones.geo_zones,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options,*fulfillment_sets.service_zones.shipping_options.rules,*fulfillment_sets.service_zones.shipping_options.shipping_profile",
}
)
const zone = stock_location?.fulfillment_sets
.find((f) => f.id === fset_id)
?.service_zones.find((z) => z.id === zone_id)
if (isError) {
throw error
}
if (!isPending && !zone) {
throw json(
{ message: `Service zone with ID ${zone_id} was not found` },
404
)
}
return (
<RouteFocusModal>
{!isPending && zone && (
<EditServiceZoneAreasForm
zone={zone}
fulfillmentSetId={fset_id}
locationId={location_id}
/>
)}
</RouteFocusModal>
)
}
@@ -16,10 +16,11 @@ import {
IconButton, IconButton,
Input, Input,
Text, Text,
toast,
} from "@medusajs/ui" } from "@medusajs/ui"
import { FulfillmentSetDTO, RegionCountryDTO, RegionDTO } from "@medusajs/types" import { FulfillmentSetDTO, RegionCountryDTO, RegionDTO } from "@medusajs/types"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { Map, XMark, XMarkMini } from "@medusajs/icons" import { XMarkMini } from "@medusajs/icons"
import { import {
RouteFocusModal, RouteFocusModal,
@@ -87,13 +88,20 @@ export function CreateServiceZoneForm({
useCreateServiceZone(locationId, fulfillmentSet.id) useCreateServiceZone(locationId, fulfillmentSet.id)
const handleSubmit = form.handleSubmit(async (data) => { const handleSubmit = form.handleSubmit(async (data) => {
await createServiceZone({ try {
name: data.name, await createServiceZone({
geo_zones: data.countries.map((iso2) => ({ name: data.name,
country_code: iso2, geo_zones: data.countries.map((iso2) => ({
type: "country", country_code: iso2,
})), type: "country",
}) })),
})
} catch (e) {
toast.error(t("general.error"), {
description: e.message,
dismissLabel: t("general.close"),
})
}
handleSuccess() handleSuccess()
}) })
@@ -201,7 +209,7 @@ export function CreateServiceZoneForm({
<RouteFocusModal.Body className="m-auto flex h-full w-full flex-col items-center divide-y overflow-hidden"> <RouteFocusModal.Body className="m-auto flex h-full w-full flex-col items-center divide-y overflow-hidden">
<SplitView open={open} onOpenChange={handleOpenChange}> <SplitView open={open} onOpenChange={handleOpenChange}>
<SplitView.Content className="mx-auto max-w-[720px]"> <SplitView.Content className="mx-auto max-w-[720px]">
<div className="container w-fit px-1 py-8"> <div className="container w-fit px-1 py-8">
<Heading className="mb-12 mt-8 text-2xl"> <Heading className="mb-12 mt-8 text-2xl">
{t("shipping.fulfillmentSet.create.title", { {t("shipping.fulfillmentSet.create.title", {
fulfillmentSet: fulfillmentSet.name, fulfillmentSet: fulfillmentSet.name,
@@ -6,7 +6,9 @@ import { StockLocationRes } from "../../../types/api-responses"
import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations" import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations"
const fulfillmentSetCreateQuery = (id: string) => ({ const fulfillmentSetCreateQuery = (id: string) => ({
queryKey: stockLocationsQueryKeys.detail(id), queryKey: stockLocationsQueryKeys.detail(id, {
fields: "*fulfillment_sets",
}),
queryFn: async () => queryFn: async () =>
client.stockLocations.retrieve(id, { client.stockLocations.retrieve(id, {
fields: "*fulfillment_sets", fields: "*fulfillment_sets",
@@ -45,7 +45,7 @@ type StepStatus = {
[key in Tab]: ProgressStatus [key in Tab]: ProgressStatus
} }
const CreateServiceZoneSchema = zod.object({ const CreateShippingOptionSchema = zod.object({
name: zod.string().min(1), name: zod.string().min(1),
price_type: zod.nativeEnum(ShippingAllocation), price_type: zod.nativeEnum(ShippingAllocation),
enabled_in_store: zod.boolean().optional(), enabled_in_store: zod.boolean().optional(),
@@ -55,7 +55,7 @@ const CreateServiceZoneSchema = zod.object({
currency_prices: zod.record(zod.string(), zod.string().optional()), currency_prices: zod.record(zod.string(), zod.string().optional()),
}) })
type CreateServiceZoneFormProps = { type CreateShippingOptionFormProps = {
zone: ServiceZoneDTO zone: ServiceZoneDTO
isReturn?: boolean isReturn?: boolean
} }
@@ -63,7 +63,7 @@ type CreateServiceZoneFormProps = {
export function CreateShippingOptionsForm({ export function CreateShippingOptionsForm({
zone, zone,
isReturn, isReturn,
}: CreateServiceZoneFormProps) { }: CreateShippingOptionFormProps) {
const { t } = useTranslation() const { t } = useTranslation()
const { handleSuccess } = useRouteModal() const { handleSuccess } = useRouteModal()
const [tab, setTab] = React.useState<Tab>(Tab.DETAILS) const [tab, setTab] = React.useState<Tab>(Tab.DETAILS)
@@ -77,7 +77,7 @@ export function CreateShippingOptionsForm({
fields: "id,currency_code", fields: "id,currency_code",
}) })
const form = useForm<zod.infer<typeof CreateServiceZoneSchema>>({ const form = useForm<zod.infer<typeof CreateShippingOptionSchema>>({
defaultValues: { defaultValues: {
name: "", name: "",
price_type: ShippingAllocation.FlatRate, price_type: ShippingAllocation.FlatRate,
@@ -87,7 +87,7 @@ export function CreateShippingOptionsForm({
region_prices: {}, region_prices: {},
currency_prices: {}, currency_prices: {},
}, },
resolver: zodResolver(CreateServiceZoneSchema), resolver: zodResolver(CreateShippingOptionSchema),
}) })
const isCalculatedPriceType = const isCalculatedPriceType =
@@ -6,7 +6,10 @@ import { StockLocationRes } from "../../../types/api-responses"
import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations" import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations"
const fulfillmentSetCreateQuery = (id: string) => ({ const fulfillmentSetCreateQuery = (id: string) => ({
queryKey: stockLocationsQueryKeys.list(), // Use the list cache key for now queryKey: stockLocationsQueryKeys.detail(id, {
fields:
"*fulfillment_sets,*fulfillment_sets.service_zones,*fulfillment_sets.service_zones.shipping_options",
}),
queryFn: async () => queryFn: async () =>
client.stockLocations.retrieve(id, { client.stockLocations.retrieve(id, {
fields: fields:
@@ -0,0 +1,319 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import React, { useEffect, useMemo, useState } from "react"
import * as zod from "zod"
import { Button, toast } from "@medusajs/ui"
import {
CurrencyDTO,
PriceDTO,
ProductVariantDTO,
RegionDTO,
ShippingOptionDTO,
} from "@medusajs/types"
import { useTranslation } from "react-i18next"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import {
getDbAmount,
getPresentationalAmount,
} from "../../../../../lib/money-amount-helpers"
import { useRegions } from "../../../../../hooks/api/regions"
import { useStore } from "../../../../../hooks/api/store.tsx"
import { useCurrencies } from "../../../../../hooks/api/currencies"
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
import { ExtendedProductDTO } from "../../../../../types/api-responses"
import { CurrencyCell } from "../../../../../components/grid/grid-cells/common/currency-cell"
import { DataGridMeta } from "../../../../../components/grid/types"
import { DataGrid } from "../../../../../components/grid/data-grid"
import { useUpdateShippingOptions } from "../../../../../hooks/api/shipping-options.ts"
const getInitialCurrencyPrices = (prices: PriceDTO[]) => {
const ret: Record<string, number> = {}
prices.forEach((p) => {
if (p.price_rules!.length) {
// this is a region price
return
}
ret[p.currency_code!] = getPresentationalAmount(
p.amount as number,
p.currency_code!
)
})
return ret
}
const getInitialRegionPrices = (prices: PriceDTO[]) => {
const ret: Record<string, number> = {}
prices.forEach((p) => {
if (p.price_rules!.length) {
const regionId = p.price_rules![0].value
ret[regionId] = getPresentationalAmount(
p.amount as number,
p.currency_code!
)
}
})
return ret
}
const EditShippingOptionPricingSchema = zod.object({
region_prices: zod.record(
zod.string(),
zod.string().or(zod.number()).optional()
),
currency_prices: zod.record(
zod.string(),
zod.string().or(zod.number()).optional()
),
})
enum ColumnType {
REGION = "region",
CURRENCY = "currency",
}
type EnabledColumnRecord = Record<string, ColumnType>
type EditShippingOptionPricingFormProps = {
shippingOption: ShippingOptionDTO & { prices: PriceDTO[] }
}
export function EditShippingOptionsPricingForm({
shippingOption,
}: EditShippingOptionPricingFormProps) {
const { t } = useTranslation()
const { handleSuccess } = useRouteModal()
const form = useForm<zod.infer<typeof EditShippingOptionPricingSchema>>({
defaultValues: {
region_prices: getInitialRegionPrices(shippingOption.prices),
currency_prices: getInitialCurrencyPrices(shippingOption.prices),
},
resolver: zodResolver(EditShippingOptionPricingSchema),
})
const { mutateAsync, isPending: isLoading } = useUpdateShippingOptions(
shippingOption.id
)
const { regions } = useRegions()
const { store, isLoading: isStoreLoading } = useStore()
const { currencies, isLoading: isCurrenciesLoading } = useCurrencies(
{
code: store?.supported_currency_codes,
},
{
enabled: !!store,
}
)
const [enabledColumns, setEnabledColumns] = useState<EnabledColumnRecord>({})
useEffect(() => {
if (
store?.default_currency_code &&
Object.keys(enabledColumns).length === 0
) {
setEnabledColumns({
...enabledColumns,
[store.default_currency_code]: ColumnType.CURRENCY,
})
}
}, [store, enabledColumns])
const columns = useColumns({
currencies,
regions,
})
const data = useMemo(
() => [[...(currencies || []), ...(regions || [])]],
[currencies, regions]
)
const handleSubmit = form.handleSubmit(async (data) => {
const currencyPrices = Object.entries(data.currency_prices)
.map(([code, value]) => {
if (value === "") {
return undefined
}
const amount = getDbAmount(Number(value), code)
const priceRecord = {
currency_code: code,
amount: amount,
}
const price = shippingOption.prices.find(
(p) => p.currency_code === code && !p.price_rules!.length
)
// if that currency price is already defined for the SO, we will do an update
if (price) {
priceRecord["id"] = price.id
}
return priceRecord
})
.filter((p) => !!p)
const regionsMap = new Map(regions.map((r) => [r.id, r.currency_code]))
const regionPrices = Object.entries(data.region_prices)
.map(([region_id, value]) => {
if (value === "") {
return undefined
}
const code = regionsMap.get(region_id)!
const amount = getDbAmount(Number(value), code)
const priceRecord = {
region_id,
amount: amount,
}
/**
* HACK - when trying to update prices which already have a region price
* we get error: `Price rule with price_id: , rule_type_id: already exist`,
* so for now, we recreate region prices.
*/
// const price = shippingOption.prices.find(
// (p) => p.price_rules?.[0]?.value === region_id
// )
//
// if (price) {
// priceRecord["id"] = price.id
// }
return priceRecord
})
.filter((p) => !!p)
try {
await mutateAsync({
prices: [...currencyPrices, ...regionPrices],
})
toast.error(t("general.success"), {
dismissLabel: t("general.close"),
})
handleSuccess()
} catch (e) {
toast.error(t("general.error"), {
description: e.message,
dismissLabel: t("general.close"),
})
}
})
const initializing =
isStoreLoading || isCurrenciesLoading || !store || !currencies
return (
<RouteFocusModal.Form form={form}>
<form
className="flex h-full flex-col overflow-hidden"
onSubmit={handleSubmit}
>
<RouteFocusModal.Header>
<div className="flex items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button
size="small"
className="whitespace-nowrap"
isLoading={isLoading}
onClick={handleSubmit}
type="button"
>
{t("actions.save")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex h-full w-fit overflow-auto">
<div
style={{ width: "100vw" }}
className="flex size-full flex-col divide-y"
>
<DataGrid
columns={columns}
data={data}
isLoading={initializing}
state={form}
/>
</div>
</RouteFocusModal.Body>
</form>
</RouteFocusModal.Form>
)
}
const columnHelper = createColumnHelper<
ExtendedProductDTO | ProductVariantDTO
>()
const useColumns = ({
currencies = [],
regions = [],
}: {
currencies?: CurrencyDTO[]
regions?: RegionDTO[]
}) => {
const { t } = useTranslation()
const colDefs: ColumnDef<ExtendedProductDTO | ProductVariantDTO>[] =
useMemo(() => {
return [
...currencies.map((currency) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: currency.code.toUpperCase(),
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currency}
meta={table.options.meta as DataGridMeta<any>}
field={`currency_prices.${currency.code}`}
/>
)
},
})
}),
...regions.map((region) => {
return columnHelper.display({
header: t("fields.priceTemplate", {
regionOrCountry: region.name,
}),
cell: ({ row, table }) => {
return (
<CurrencyCell
currency={currencies.find(
(c) => c.code === region.currency_code
)}
meta={table.options.meta as DataGridMeta<any>}
field={`region_prices.${region.id}`}
/>
)
},
})
}),
]
}, [t, currencies, regions])
return colDefs
}
@@ -0,0 +1 @@
export * from "./edit-shipping-options-pricing-form.tsx"
@@ -0,0 +1 @@
export { ShippingOptionsEditPricing as Component } from "./shipping-options-edit-pricing"
@@ -0,0 +1,30 @@
import { useParams } from "react-router-dom"
import { RouteFocusModal } from "../../../components/route-modal"
import { useShippingOptions } from "../../../hooks/api/shipping-options"
import { EditShippingOptionsPricingForm } from "./components/create-shipping-options-form"
export function ShippingOptionsEditPricing() {
const { so_id } = useParams()
const { shipping_options, isPending } = useShippingOptions({
// TODO: change this when GET option by id endpoint is implemented
id: [so_id],
fields: "*prices,*prices.price_rules",
limit: 999,
})
const shippingOption = shipping_options?.find((so) => so.id === so_id)
if (!isPending && !shippingOption) {
throw new Error(`Shipping option with id: ${so_id} not found`)
}
return (
<RouteFocusModal>
{shippingOption && (
<EditShippingOptionsPricingForm shippingOption={shippingOption} />
)}
</RouteFocusModal>
)
}
+2 -2
View File
@@ -52,13 +52,13 @@ git checkout package.json; npm install --force
You can prevent the automatic dependencies scan and instead specify a list of You can prevent the automatic dependencies scan and instead specify a list of
packages you want to link by using the `--packages` option: packages you want to link by using the `--packages` option:
`medusa-dev --packages @medusajs/medusa medusa-interfaces` `medusa-dev --packages @medusajs/medusa`
#### `--scan-once` #### `--scan-once`
With this flag, the tool will do an initial scan and copy and then quit. This is With this flag, the tool will do an initial scan and copy and then quit. This is
useful for setting up automated testing/builds of Medusa projects from the latest useful for setting up automated testing/builds of Medusa projects from the latest
code. code.
#### `--quiet` #### `--quiet`
@@ -1,2 +1,3 @@
export * from "./steps/remove-remote-links" export * from "./steps/remove-remote-links"
export * from "./steps/use-remote-query" export * from "./steps/use-remote-query"
export * from "./steps/create-remote-links"
@@ -0,0 +1,29 @@
import { LinkDefinition, RemoteLink } from "@medusajs/modules-sdk"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
import { ContainerRegistrationKeys } from "@medusajs/utils"
type CreateRemoteLinksStepInput = LinkDefinition[]
export const createLinksStepId = "create-links"
export const createLinkStep = createStep(
createLinksStepId,
async (data: CreateRemoteLinksStepInput, { container }) => {
const link = container.resolve<RemoteLink>(
ContainerRegistrationKeys.REMOTE_LINK
)
await link.create(data)
return new StepResponse(data, data)
},
async (createdLinks, { container }) => {
if (!createdLinks) {
return
}
const link = container.resolve<RemoteLink>(
ContainerRegistrationKeys.REMOTE_LINK
)
await link.dismiss(createdLinks)
}
)
@@ -0,0 +1,29 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CreateOrderReturnDTO, IOrderModuleService } from "@medusajs/types"
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
type CreateReturnStepInput = CreateOrderReturnDTO
export const createReturnStepId = "create-return"
export const createReturnStep = createStep(
createReturnStepId,
async (data: CreateReturnStepInput, { container }) => {
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
const created = await service.createReturn(data)
return new StepResponse(created, created)
},
async (createdId, { container }) => {
if (!createdId) {
return
}
const service = container.resolve<IOrderModuleService>(
ModuleRegistrationName.ORDER
)
// TODO: delete return
}
)
@@ -6,12 +6,12 @@ import {
OrderShippingMethodDTO, OrderShippingMethodDTO,
OrderWorkflowDTO, OrderWorkflowDTO,
ShippingTaxLineDTO, ShippingTaxLineDTO,
TaxCalculationContext,
TaxableItemDTO, TaxableItemDTO,
TaxableShippingDTO, TaxableShippingDTO,
TaxCalculationContext,
} from "@medusajs/types" } from "@medusajs/types"
import { MedusaError } from "@medusajs/utils" import { MedusaError } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk" import { createStep, StepResponse } from "@medusajs/workflows-sdk"
interface StepInput { interface StepInput {
order: OrderWorkflowDTO order: OrderWorkflowDTO
@@ -104,8 +104,8 @@ export const getOrderItemTaxLinesStep = createStep(
async (data: StepInput, { container }) => { async (data: StepInput, { container }) => {
const { const {
order, order,
items, items = [],
shipping_methods: shippingMethods, shipping_methods: shippingMethods = [],
force_tax_calculation: forceTaxCalculation = false, force_tax_calculation: forceTaxCalculation = false,
} = data } = data
const taxService = container.resolve<ITaxModuleService>( const taxService = container.resolve<ITaxModuleService>(
@@ -123,15 +123,19 @@ export const getOrderItemTaxLinesStep = createStep(
return new StepResponse(stepResponseData) return new StepResponse(stepResponseData)
} }
stepResponseData.lineItemTaxLines = (await taxService.getTaxLines( if (items.length) {
normalizeLineItemsForTax(order, items), stepResponseData.lineItemTaxLines = (await taxService.getTaxLines(
taxContext normalizeLineItemsForTax(order, items),
)) as ItemTaxLineDTO[] taxContext
)) as ItemTaxLineDTO[]
}
stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines( if (shippingMethods.length) {
normalizeLineItemsForShipping(order, shippingMethods), stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines(
taxContext normalizeLineItemsForShipping(order, shippingMethods),
)) as ShippingTaxLineDTO[] taxContext
)) as ShippingTaxLineDTO[]
}
return new StepResponse(stepResponseData) return new StepResponse(stepResponseData)
} }
@@ -0,0 +1,339 @@
import {
CreateOrderShippingMethodDTO,
FulfillmentWorkflow,
OrderDTO,
OrderWorkflow,
ShippingOptionDTO,
WithCalculatedPrice,
} from "@medusajs/types"
import {
createWorkflow,
transform,
WorkflowData,
} from "@medusajs/workflows-sdk"
import { createLinkStep, useRemoteQueryStep } from "../../common"
import {
arrayDifference,
ContainerRegistrationKeys,
isDefined,
MathBN,
MedusaError,
Modules,
} from "@medusajs/utils"
import { updateOrderTaxLinesStep } from "../steps"
import { createReturnStep } from "../steps/create-return"
import { createFulfillmentWorkflow } from "../../fulfillment"
function throwIfOrderIsCancelled({ order }: { order: OrderDTO }) {
// TODO: need work, check canceled
if (false /*order.canceled_at*/) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Order with id ${order.id} has been cancelled.`
)
}
}
function throwIfItemsDoesNotExistsInOrder({
order,
inputItems,
}: {
order: Pick<OrderDTO, "id" | "items">
inputItems: OrderWorkflow.CreateOrderReturnWorkflowInput["items"]
}) {
const orderItemIds = order.items?.map((i) => i.id) ?? []
const inputItemIds = inputItems.map((i) => i.id)
const diff = arrayDifference(inputItemIds, orderItemIds)
if (diff.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Items with ids ${diff.join(", ")} does not exist in order with id ${
order.id
}.`
)
}
}
function validateReturnReasons(
{
orderId,
inputItems,
}: {
orderId: string
inputItems: OrderWorkflow.CreateOrderReturnWorkflowInput["items"]
},
{ container }
) {
const reasonIds = inputItems.map((i) => i.reason_id).filter(Boolean)
if (!reasonIds.length) {
return
}
const remoteQuery = container.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const returnReasons = remoteQuery({
entry_point: "return_reasons",
fields: ["return_reason_children.*"],
variables: { id: [inputItems.map((item) => item.reason_id)] },
})
const reasons = returnReasons.map((r) => r.id)
const hasInvalidReasons = reasons.filter(
// We do not allow for root reason to be applied
(reason) => reason.return_reason_children.length > 0
)
const hasNonExistingReasons = arrayDifference(reasonIds, reasons)
if (hasNonExistingReasons.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Return reason with id ${hasNonExistingReasons.join(
", "
)} does not exists.`
)
}
if (hasInvalidReasons.length()) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot apply return reason with id ${hasInvalidReasons.join(
", "
)} to order with id ${orderId}. Return reason has nested reasons.`
)
}
}
function prepareShippingMethodData({
orderId,
inputShippingOption,
returnShippingOption,
}: {
orderId: string
inputShippingOption: OrderWorkflow.CreateOrderReturnWorkflowInput["return_shipping"]
returnShippingOption: ShippingOptionDTO & WithCalculatedPrice
}) {
const obj: CreateOrderShippingMethodDTO = {
name: returnShippingOption.name,
order_id: orderId,
shipping_option_id: returnShippingOption.id,
amount: 0,
data: {},
// Computed later in the flow
tax_lines: [],
adjustments: [],
}
if (isDefined(inputShippingOption.price) && inputShippingOption.price >= 0) {
obj.amount = inputShippingOption.price
} else {
if (returnShippingOption.price_type === "calculated") {
// TODO: retrieve calculated price and assign to amount
} else {
obj.amount = returnShippingOption.calculated_price.calculated_amount
}
}
return obj
}
function validateCustomRefundAmount({
order,
refundAmount,
}: {
order: Pick<OrderDTO, "item_total">
refundAmount?: number
}) {
// validate that the refund prop input is less than order.item_total (item total)
// TODO: Probably this amount should be retrieved from the payments linked to the order
if (refundAmount && MathBN.gt(refundAmount, order.item_total)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Refund amount cannot be greater than order total.`
)
}
}
function prepareFulfillmentData({
order,
input,
returnShippingOption,
}: {
order: OrderDTO
input: OrderWorkflow.CreateOrderReturnWorkflowInput
returnShippingOption: {
id: string
provider_id: string
service_zone: { fulfillment_set: { location?: { id: string } } }
}
}) {
const inputItems = input.items
const orderItemsMap = new Map<string, Required<OrderDTO>["items"][0]>(
order.items!.map((i) => [i.id, i])
)
const fulfillmentItems = inputItems.map((i) => {
const orderItem = orderItemsMap.get(i.id)!
return {
line_item_id: i.id,
quantity: i.quantity,
return_quantity: i.quantity,
title: orderItem.variant_title ?? orderItem.title,
sku: orderItem.variant_sku || "",
barcode: orderItem.variant_barcode || "",
} as FulfillmentWorkflow.CreateFulfillmentItemWorkflowDTO
})
let locationId: string | undefined = input.location_id
if (!locationId) {
locationId = returnShippingOption.service_zone.fulfillment_set.location?.id
}
if (!locationId) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Cannot create return without stock location, either provide a location or you should link the shipping option ${returnShippingOption.id} to a stock location.`
)
}
return {
input: {
location_id: locationId,
provider_id: returnShippingOption.provider_id,
shipping_option_id: input.return_shipping.option_id,
items: fulfillmentItems,
labels: [] as FulfillmentWorkflow.CreateFulfillmentLabelWorkflowDTO[],
delivery_address: order.shipping_address ?? ({} as any), // TODO: should it be the stock location address?
order: {} as FulfillmentWorkflow.CreateFulfillmentOrderWorkflowDTO, // TODO see what todo here, is that even necessary?
},
}
}
function prepareReturnShippingOptionQueryVariables({
order,
input,
}: {
order: {
currency_code: string
region_id?: string
}
input: {
return_shipping: OrderWorkflow.CreateOrderReturnWorkflowInput["return_shipping"]
}
}) {
const variables = {
id: input.return_shipping.option_id,
calculated_price: {
context: {
currency_code: order.currency_code,
},
},
}
if (order.region_id) {
variables.calculated_price.context["region_id"] = order.region_id
}
return variables
}
export const createReturnOrderWorkflowId = "create-return-order"
export const createReturnOrderWorkflow = createWorkflow(
createReturnOrderWorkflowId,
(
input: WorkflowData<OrderWorkflow.CreateOrderReturnWorkflowInput>
): WorkflowData<void> => {
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: [
"id",
"region_id",
"currency_code",
"total",
"item_total",
"items.*",
],
variables: { id: input.order_id },
list: false,
throw_if_key_not_found: true,
})
transform({ order }, throwIfOrderIsCancelled)
transform(
{ order, inputItems: input.items },
throwIfItemsDoesNotExistsInOrder
)
transform(
{ orderId: input.order_id, inputItems: input.items },
validateReturnReasons
)
transform(
{ order, refundAmount: input.refund_amount },
validateCustomRefundAmount
)
const returnShippingOptionsVariables = transform(
{ input, order },
prepareReturnShippingOptionQueryVariables
)
const returnShippingOption = useRemoteQueryStep({
entry_point: "shipping_options",
fields: [
"id",
"price_type",
"name",
"provider_id",
"calculated_price.calculated_amount",
"service_zone.fulfillment_set.location.id",
],
variables: returnShippingOptionsVariables,
list: false,
throw_if_key_not_found: true,
}).config({ name: "return-shipping-option" })
const shippingMethodData = transform(
{
orderId: input.order_id,
inputShippingOption: input.return_shipping,
returnShippingOption,
},
prepareShippingMethodData
)
createReturnStep({
order_id: input.order_id,
items: input.items,
shipping_method: shippingMethodData,
created_by: input.created_by,
})
updateOrderTaxLinesStep({
order_id: input.order_id,
shipping_methods: [shippingMethodData as any], // The types does not seems correct in that step and expect too many things compared to the actual needs
})
const fulfillmentData = transform(
{ order, input, returnShippingOption },
prepareFulfillmentData
)
const fulfillment = createFulfillmentWorkflow.runAsStep(fulfillmentData)
// TODO call the createReturn from the fulfillment provider
const link = transform(
{ order_id: input.order_id, fulfillment },
(data) => {
return [
{
[Modules.ORDER]: { order_id: data.order_id },
[Modules.FULFILLMENT]: { fulfillment_id: data.fulfillment.id },
},
]
}
)
createLinkStep(link)
}
)
@@ -1,2 +1,3 @@
export * from "./create-orders" export * from "./create-orders"
export * from "./update-tax-lines" export * from "./update-tax-lines"
export * from "./create-return"
+1 -1
View File
@@ -14,7 +14,7 @@ export type DeleteEntityInput = {
} }
export type RestoreEntityInput = DeleteEntityInput export type RestoreEntityInput = DeleteEntityInput
type LinkDefinition = { export type LinkDefinition = {
[moduleName: string]: { [moduleName: string]: {
[fieldName: string]: string [fieldName: string]: string
} }
+1
View File
@@ -3,3 +3,4 @@ export * from "./rule"
export * from "./batch" export * from "./batch"
export * from "./config-module" export * from "./config-module"
export * from "./medusa-container" export * from "./medusa-container"
export * from "./with-calculated"
@@ -0,0 +1,3 @@
export interface WithCalculatedPrice {
calculated_price: { calculated_amount: number }
}
@@ -120,6 +120,14 @@ export interface UpdateShippingOptionDTO {
id: string id: string
} }
)[] )[]
/**
* The shipping option pricing
*/
prices: (
| { currency_code: string; amount: number; id?: string }
| { region_id: string; amount: number; id?: string }
)[]
} }
/** /**
+14 -1
View File
@@ -2,6 +2,19 @@ import { BaseFilterable } from "../dal"
import { OperatorMap } from "../dal/utils" import { OperatorMap } from "../dal/utils"
import { BigNumberRawValue, BigNumberValue } from "../totals" import { BigNumberRawValue, BigNumberValue } from "../totals"
export type ChangeActionType =
| "CANCEL"
| "CANCEL_RETURN"
| "FULFILL_ITEM"
| "ITEM_ADD"
| "ITEM_REMOVE"
| "RECEIVE_DAMAGED_RETURN_ITEM"
| "RECEIVE_RETURN_ITEM"
| "RETURN_ITEM"
| "SHIPPING_ADD"
| "SHIP_ITEM"
| "WRITE_OFF_ITEM"
export type OrderSummaryDTO = { export type OrderSummaryDTO = {
total: BigNumberValue total: BigNumberValue
subtotal: BigNumberValue subtotal: BigNumberValue
@@ -1187,7 +1200,7 @@ export interface OrderChangeActionDTO {
/** /**
* The action of the order change action * The action of the order change action
*/ */
action: string action: ChangeActionType
/** /**
* The details of the order change action * The details of the order change action
*/ */
+2 -1
View File
@@ -1,5 +1,6 @@
import { BigNumberInput } from "../totals" import { BigNumberInput } from "../totals"
import { import {
ChangeActionType,
OrderItemDTO, OrderItemDTO,
OrderLineItemDTO, OrderLineItemDTO,
OrderReturnReasonDTO, OrderReturnReasonDTO,
@@ -300,7 +301,7 @@ export interface CreateOrderChangeActionDTO {
version?: number version?: number
reference?: string reference?: string
reference_id?: string reference_id?: string
action: string action: ChangeActionType
internal_note?: string internal_note?: string
amount?: BigNumberInput amount?: BigNumberInput
details?: Record<string, unknown> details?: Record<string, unknown>
@@ -9,3 +9,4 @@ export * as ProductCategoryWorkflow from "./product-category"
export * as RegionWorkflow from "./region" export * as RegionWorkflow from "./region"
export * as ReservationWorkflow from "./reservation" export * as ReservationWorkflow from "./reservation"
export * as UserWorkflow from "./user" export * as UserWorkflow from "./user"
export * as OrderWorkflow from "./order"
@@ -0,0 +1,26 @@
import { BigNumberInput } from "../../totals"
interface CreateOrderReturnItem {
id: string
quantity: BigNumberInput
internal_note?: string
reason_id?: string
metadata?: Record<string, any>
}
export interface CreateOrderReturnWorkflowInput {
order_id: string
created_by?: string // The id of the authenticated user
items: CreateOrderReturnItem[]
return_shipping: {
option_id: string
price?: number
}
note?: string
receive_now?: boolean
refund_amount?: number
/**
* Default fallback to the shipping option location id
*/
location_id?: string
}
@@ -0,0 +1 @@
export * from "./create-return-order"
+6
View File
@@ -80,4 +80,10 @@ export const LINKS = {
Modules.PAYMENT, Modules.PAYMENT,
"payment_collection_id" "payment_collection_id"
), ),
OrderFulfillment: composeLinkName(
Modules.ORDER,
"order_id",
Modules.FULFILLMENT,
"fulfillment_id"
),
} }
@@ -57,7 +57,7 @@ export async function resolveValue(input, transactionContext) {
) )
if (typeof parentRef[key] === "object") { if (typeof parentRef[key] === "object") {
await unwrapInput(parentRef[key], parentRef[key]) parentRef[key] = await unwrapInput(parentRef[key], parentRef[key])
} }
} }
@@ -201,7 +201,7 @@ export type ReturnWorkflow<
runAsStep: ({ runAsStep: ({
input, input,
}: { }: {
input: TData input: TData | WorkflowData<TData>
}) => ReturnType<StepFunction<TData, TResult>> }) => ReturnType<StepFunction<TData, TResult>>
run: <TDataOverride = undefined, TResultOverride = undefined>( run: <TDataOverride = undefined, TResultOverride = undefined>(
...args: Parameters< ...args: Parameters<
-13
View File
@@ -1,13 +0,0 @@
{
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-instanceof",
["@babel/plugin-transform-runtime", { "regenerator": true }]
],
"presets": ["@babel/preset-env"],
"env": {
"test": {
"plugins": ["@babel/plugin-transform-runtime"]
}
}
}
-7
View File
@@ -1,7 +0,0 @@
dist/
node_modules/
.DS_store
.env*
/*.js
!index.js
yarn.lock
-339
View File
@@ -1,339 +0,0 @@
# Change Log
## 1.3.9
### Patch Changes
- [#6700](https://github.com/medusajs/medusa/pull/6700) [`8f8a4f9b13`](https://github.com/medusajs/medusa/commit/8f8a4f9b1353087d98f6cc75346d43a7f49901a8) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Version all modules to allow for initial testing
## 1.3.8
### Patch Changes
- [#5869](https://github.com/medusajs/medusa/pull/5869) [`45996d58a2`](https://github.com/medusajs/medusa/commit/45996d58a2665d72335faad11bea958f8da74195) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(medusa, interfaces, utils, webshiper): Uniformise class checks
## 1.3.7
### Patch Changes
- [#3041](https://github.com/medusajs/medusa/pull/3041) [`121b42acf`](https://github.com/medusajs/medusa/commit/121b42acfe98c12dd593f9b1f2072ff0f3b61724) Thanks [@riqwan](https://github.com/riqwan)! - chore(medusa): Typeorm fixes / enhancements
- upgrade typeorm from 0.2.51 to 0.3.11
- Plugin repository loader to work with Typeorm update
## 1.3.7-rc.0
### Patch Changes
- [#3041](https://github.com/medusajs/medusa/pull/3041) [`121b42acf`](https://github.com/medusajs/medusa/commit/121b42acfe98c12dd593f9b1f2072ff0f3b61724) Thanks [@riqwan](https://github.com/riqwan)! - chore(medusa): Typeorm fixes / enhancements
- upgrade typeorm from 0.2.51 to 0.3.11
- Plugin repository loader to work with Typeorm update
## 1.3.6
### Patch Changes
- [#3217](https://github.com/medusajs/medusa/pull/3217) [`8c5219a31`](https://github.com/medusajs/medusa/commit/8c5219a31ef76ee571fbce84d7d57a63abe56eb0) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Fix npm packages files included
- Updated dependencies [[`8c5219a31`](https://github.com/medusajs/medusa/commit/8c5219a31ef76ee571fbce84d7d57a63abe56eb0)]:
- medusa-core-utils@1.1.39
## 1.3.5
### Patch Changes
- [#3185](https://github.com/medusajs/medusa/pull/3185) [`08324355a`](https://github.com/medusajs/medusa/commit/08324355a4466b017a0bc7ab1d333ee3cd27b8c4) Thanks [@olivermrbl](https://github.com/olivermrbl)! - chore: Patches all dependencies + minor bumps `winston` to include a [fix for a significant memory leak](https://github.com/winstonjs/winston/pull/2057)
- Updated dependencies [[`08324355a`](https://github.com/medusajs/medusa/commit/08324355a4466b017a0bc7ab1d333ee3cd27b8c4)]:
- medusa-core-utils@1.1.38
## 1.3.4
### Patch Changes
- [#3025](https://github.com/medusajs/medusa/pull/3025) [`93d0dc1bd`](https://github.com/medusajs/medusa/commit/93d0dc1bdcb54cf6e87428a7bb9b0dac196b4de2) Thanks [@adrien2p](https://github.com/adrien2p)! - fix(medusa): test, build and watch scripts
## 1.3.3
### Patch Changes
- [#1962](https://github.com/medusajs/medusa/pull/1962) [`c97ccd3fb`](https://github.com/medusajs/medusa/commit/c97ccd3fb5dbe796b0e4fbf37def5bb6e8201557) Thanks [@pKorsholm](https://github.com/pKorsholm)! - Convert FulfillmentService to TypeScript
## 1.3.2
### Patch Changes
- [#1914](https://github.com/medusajs/medusa/pull/1914) [`1dec44287`](https://github.com/medusajs/medusa/commit/1dec44287df5ac69b4c5769b59f9ebef58d3da68) Thanks [@fPolic](https://github.com/fPolic)! - Version bump due to missing changesets in merged PRs
* [#1874](https://github.com/medusajs/medusa/pull/1874) [`b8ddb31f6`](https://github.com/medusajs/medusa/commit/b8ddb31f6fe296a11d2d988276ba8e991c37fa9b) Thanks [@adrien2p](https://github.com/adrien2p)! - Move search indexing into a separate subscriber to defer the work load
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.3.0...medusa-interfaces@1.3.1) (2022-07-05)
### Bug Fixes
- **medusa:** Remove deps `mongoose` + `mongodb` ([#1218](https://github.com/medusajs/medusa/issues/1218)) ([c76e23e](https://github.com/medusajs/medusa/commit/c76e23e84dd8cb08c3c709f9f95c4c17b9685439))
# [1.3.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.2.1...medusa-interfaces@1.3.0) (2022-05-01)
### Bug Fixes
- ensures no duplicate tax lines when completing cart ([#1262](https://github.com/medusajs/medusa/issues/1262)) ([607a382](https://github.com/medusajs/medusa/commit/607a382b4ee190c25eafa345674b55b74a7d6349))
## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.1) (2022-02-28)
### Bug Fixes
- atomic phase error handler ([#1104](https://github.com/medusajs/medusa/issues/1104)) ([f983cfa](https://github.com/medusajs/medusa/commit/f983cfada675b9c2ad89f1dea37f862673383f54))
# [1.2.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.0) (2022-02-25)
### Bug Fixes
- atomic phase error handler ([#1104](https://github.com/medusajs/medusa/issues/1104)) ([62c263c](https://github.com/medusajs/medusa/commit/62c263c36080541023fda5ae33a458c58cbaeb1e))
## [1.1.34](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.33...medusa-interfaces@1.1.34) (2022-02-06)
### Bug Fixes
- release ([fc3fbc8](https://github.com/medusajs/medusa/commit/fc3fbc897fad5c8a5d3eea828ac7277fba9d70af))
## [1.1.33](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.32...medusa-interfaces@1.1.33) (2022-02-06)
**Note:** Version bump only for package medusa-interfaces
## [1.1.32](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.31...medusa-interfaces@1.1.32) (2021-12-08)
**Note:** Version bump only for package medusa-interfaces
## [1.1.31](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.30...medusa-interfaces@1.1.31) (2021-11-23)
**Note:** Version bump only for package medusa-interfaces
## [1.1.30](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.29...medusa-interfaces@1.1.30) (2021-11-22)
**Note:** Version bump only for package medusa-interfaces
## [1.1.29](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.28...medusa-interfaces@1.1.29) (2021-11-19)
**Note:** Version bump only for package medusa-interfaces
## [1.1.28](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.27...medusa-interfaces@1.1.28) (2021-11-19)
### Features
- Allow retrieval of soft-deleted products ([#723](https://github.com/medusajs/medusa/issues/723)) ([1e50aee](https://github.com/medusajs/medusa/commit/1e50aee4feb55092560dd4a9c51a0671363e8576))
- Typescript for API layer ([#817](https://github.com/medusajs/medusa/issues/817)) ([373532e](https://github.com/medusajs/medusa/commit/373532ecbc8196f47e71af95a8cf82a14a4b1f9e))
## [1.1.27](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.26...medusa-interfaces@1.1.27) (2021-10-18)
**Note:** Version bump only for package medusa-interfaces
## [1.1.26](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.25...medusa-interfaces@1.1.26) (2021-10-18)
**Note:** Version bump only for package medusa-interfaces
## [1.1.25](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.23...medusa-interfaces@1.1.25) (2021-10-18)
### Bug Fixes
- filter type in jsdoc ([c3a6045](https://github.com/medusajs/medusa/commit/c3a6045dd889a0d32d9e7f6e806d96d8333ea0a5))
- product ordering ([57a6612](https://github.com/medusajs/medusa/commit/57a6612e845c078aec023d0cc49d6bfc175a1b37))
- use type to choose transformer before adding or replacing documents ([24eecd2](https://github.com/medusajs/medusa/commit/24eecd2922e0c3425f2d43549b3227c756820387))
## [1.1.24](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.23...medusa-interfaces@1.1.24) (2021-10-18)
### Bug Fixes
- filter type in jsdoc ([c3a6045](https://github.com/medusajs/medusa/commit/c3a6045dd889a0d32d9e7f6e806d96d8333ea0a5))
- product ordering ([57a6612](https://github.com/medusajs/medusa/commit/57a6612e845c078aec023d0cc49d6bfc175a1b37))
- use type to choose transformer before adding or replacing documents ([24eecd2](https://github.com/medusajs/medusa/commit/24eecd2922e0c3425f2d43549b3227c756820387))
## [1.1.23](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.22...medusa-interfaces@1.1.23) (2021-09-15)
**Note:** Version bump only for package medusa-interfaces
## [1.1.22](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.21...medusa-interfaces@1.1.22) (2021-09-14)
**Note:** Version bump only for package medusa-interfaces
## [1.1.21](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.20...medusa-interfaces@1.1.21) (2021-08-05)
**Note:** Version bump only for package medusa-interfaces
## [1.1.20](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.19...medusa-interfaces@1.1.20) (2021-07-26)
**Note:** Version bump only for package medusa-interfaces
## [1.1.19](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.17...medusa-interfaces@1.1.19) (2021-07-15)
**Note:** Version bump only for package medusa-interfaces
## [1.1.18](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.17...medusa-interfaces@1.1.18) (2021-07-15)
**Note:** Version bump only for package medusa-interfaces
## [1.1.17](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.16...medusa-interfaces@1.1.17) (2021-07-02)
**Note:** Version bump only for package medusa-interfaces
## [1.1.16](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.15...medusa-interfaces@1.1.16) (2021-06-22)
### Bug Fixes
- release assist ([668e8a7](https://github.com/medusajs/medusa/commit/668e8a740200847fc2a41c91d2979097f1392532))
## [1.1.15](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.14...medusa-interfaces@1.1.15) (2021-06-09)
**Note:** Version bump only for package medusa-interfaces
## [1.1.14](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.13...medusa-interfaces@1.1.14) (2021-06-09)
**Note:** Version bump only for package medusa-interfaces
## [1.1.13](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.12...medusa-interfaces@1.1.13) (2021-06-09)
**Note:** Version bump only for package medusa-interfaces
## [1.1.12](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.11...medusa-interfaces@1.1.12) (2021-06-09)
**Note:** Version bump only for package medusa-interfaces
## [1.1.11](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.10...medusa-interfaces@1.1.11) (2021-06-08)
**Note:** Version bump only for package medusa-interfaces
## [1.1.10](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.7...medusa-interfaces@1.1.10) (2021-04-28)
**Note:** Version bump only for package medusa-interfaces
## [1.1.9](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.8...medusa-interfaces@1.1.9) (2021-04-20)
**Note:** Version bump only for package medusa-interfaces
## [1.1.8](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.7...medusa-interfaces@1.1.8) (2021-04-20)
**Note:** Version bump only for package medusa-interfaces
## [1.1.7](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.6...medusa-interfaces@1.1.7) (2021-04-13)
**Note:** Version bump only for package medusa-interfaces
## [1.1.6](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.5...medusa-interfaces@1.1.6) (2021-04-09)
**Note:** Version bump only for package medusa-interfaces
## [1.1.5](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.4...medusa-interfaces@1.1.5) (2021-03-30)
**Note:** Version bump only for package medusa-interfaces
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.3...medusa-interfaces@1.1.4) (2021-03-17)
**Note:** Version bump only for package medusa-interfaces
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.1...medusa-interfaces@1.1.3) (2021-03-17)
### Features
- **medusa:** Add support for filtering with gt, lt, gte and lte ([#190](https://github.com/medusajs/medusa/issues/190)) ([dd0491f](https://github.com/medusajs/medusa/commit/dd0491f52132aed24f642589b12fcf636b719580))
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.1...medusa-interfaces@1.1.2) (2021-03-17)
### Features
- **medusa:** Add support for filtering with gt, lt, gte and lte ([#190](https://github.com/medusajs/medusa/issues/190)) ([dd0491f](https://github.com/medusajs/medusa/commit/dd0491f52132aed24f642589b12fcf636b719580))
## [1.1.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.0...medusa-interfaces@1.1.1) (2021-02-17)
### Features
- notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d))
# [1.1.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.14...medusa-interfaces@1.1.0) (2021-01-26)
**Note:** Version bump only for package medusa-interfaces
## [1.0.14](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.13...medusa-interfaces@1.0.14) (2020-11-24)
**Note:** Version bump only for package medusa-interfaces
## [1.0.13](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.12...medusa-interfaces@1.0.13) (2020-10-20)
### Features
- **medusa-interfaces:** Adds schema options to base model ([cc23a3b](https://github.com/medusajs/medusa/commit/cc23a3b0706c41ec57bb25ea3de9c6e39bd04f31))
## [1.0.12](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.11...medusa-interfaces@1.0.12) (2020-10-14)
### Features
- return shipping and flow ([#125](https://github.com/medusajs/medusa/issues/125)) ([c1e821d](https://github.com/medusajs/medusa/commit/c1e821d9d4d33756c7309e5cf110d7aa9b67297d))
## 1.0.11 (2020-10-05)
### Features
- webshipper ([#118](https://github.com/medusajs/medusa/issues/118)) ([893a7f6](https://github.com/medusajs/medusa/commit/893a7f69afea67e854a67fc3b92c8a10c9c1b75c))
## 1.0.10 (2020-09-09)
### Bug Fixes
- updates license ([db519fb](https://github.com/medusajs/medusa/commit/db519fbaa6f8ad02c19cbecba5d4f28ba1ee81aa))
## 1.0.7 (2020-09-07)
## 1.0.1 (2020-09-05)
## 1.0.1-beta.0 (2020-09-04)
# 1.0.0 (2020-09-03)
# 1.0.0-alpha.30 (2020-08-28)
# 1.0.0-alpha.27 (2020-08-27)
### Features
- **pagination:** Adds MVP pagination to orders and products for admin routes ([9dc6999](https://github.com/medusajs/medusa/commit/9dc6999a9a0c11d33eb9affa953ad1b44bd5e8b8))
# 1.0.0-alpha.24 (2020-08-27)
# 1.0.0-alpha.3 (2020-08-20)
# 1.0.0-alpha.2 (2020-08-20)
# 1.0.0-alpha.1 (2020-08-20)
# 1.0.0-alpha.0 (2020-08-20)
### Reverts
- Revert "[medusa-interfaces] : Adds decorator functionality to BaseService (#39)" (#41) ([2273cc5](https://github.com/medusajs/medusa/commit/2273cc519ad4d6ae16157173aba3955d16745e1d)), closes [#39](https://github.com/medusajs/medusa/issues/39) [#41](https://github.com/medusajs/medusa/issues/41)
# 0.3.0 (2020-04-06)
# 0.2.0 (2020-04-06)
# 0.2.0-alpha.0 (2020-04-04)
## 0.1.6-alpha.0 (2020-03-24)
## 0.1.5-alpha.0 (2020-03-24)
## 0.1.4-alpha.0 (2020-03-24)
## 0.1.3-alpha.0 (2020-03-24)
## 0.1.2-alpha.0 (2020-03-24)
## 0.1.1-alpha.0 (2020-03-24)
# 0.1.0-alpha.0 (2020-03-24)
## [1.0.10](https://github.com/medusajs/medusa/compare/v1.0.9...v1.0.10) (2020-09-09)
### Bug Fixes
- updates license ([db519fb](https://github.com/medusajs/medusa/commit/db519fbaa6f8ad02c19cbecba5d4f28ba1ee81aa))
-43
View File
@@ -1,43 +0,0 @@
{
"name": "medusa-interfaces",
"version": "1.3.9",
"description": "Core interfaces for Medusa",
"main": "dist/index.js",
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-interfaces"
},
"engines": {
"node": ">=16"
},
"scripts": {
"prepare": "cross-env NODE_ENV=production yarn run build",
"test": "jest --passWithNoTests src",
"build": "rimraf dist && babel src --out-dir dist --ignore '**/__tests__','**/__mocks__'",
"watch": "babel -w src --out-dir dist --ignore '**/__tests__','**/__mocks__'"
},
"files": [
"dist"
],
"author": "Sebastian Rindom",
"license": "MIT",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1",
"jest": "^25.5.4",
"medusa-core-utils": "^1.2.1",
"medusa-test-utils": "^1.1.42",
"rimraf": "^5.0.1",
"typescript": "^4.4.4"
},
"gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e"
}
@@ -1,66 +0,0 @@
import BaseService from "../base-service"
describe("BaseService", () => {
describe("addDecorator", () => {
const baseService = new BaseService()
it("successfully adds decorator", () => {
baseService.addDecorator(obj => {
return (obj.decorator1 = true)
})
expect(baseService.decorators_.length).toEqual(1)
})
it("throws if decorator is not a function", () => {
expect(() => baseService.addDecorator("not a function")).toThrow(
"Decorators must be of type function"
)
})
})
describe("runDecorators_", () => {
it("returns success when passwords match", async () => {
const baseService = new BaseService()
baseService.addDecorator(obj => {
obj.decorator1 = true
return obj
})
baseService.addDecorator(obj => {
obj.decorator2 = true
return obj
})
const result = await baseService.runDecorators_({ data: "initial" })
expect(result).toEqual({
data: "initial",
decorator1: true,
decorator2: true,
})
})
it("skips failing decorator", async () => {
const baseService = new BaseService()
baseService.addDecorator(obj => {
obj.decorator1 = true
return obj
})
baseService.addDecorator(obj => {
return Promise.reject("fail")
})
baseService.addDecorator(obj => {
obj.decorator3 = true
return Promise.resolve(obj)
})
const result = await baseService.runDecorators_({ data: "initial" })
expect(result).toEqual({
data: "initial",
decorator1: true,
decorator3: true,
})
})
})
})
@@ -1,174 +0,0 @@
// Import from dist to avoid circular deps which result in the base service to be undefined
import {
buildQuery,
setMetadata,
validateId,
} from "@medusajs/medusa/dist/utils"
/**
* Common functionality for Services
* @interface
* @deprecated use TransactionBaseService from @medusajs/medusa instead
*/
export default class BaseService {
constructor() {
this.decorators_ = []
}
withTransaction() {
console.log("WARN: withTransaction called without custom implementation")
return this
}
/**
* Used to build TypeORM queries.
*/
buildQuery_(selector, config = {}) {
return buildQuery(selector, config)
}
/**
* Confirms whether a given raw id is valid. Fails if the provided
* id is null or undefined. The validate function takes an optional config
* param, to support checking id prefix and length.
* @param {string} rawId - the id to validate.
* @param {object?} config - optional config
* @returns {string} the rawId given that nothing failed
*/
validateId_(rawId, config = {}) {
return validateId(rawId, config)
}
shouldRetryTransaction(err) {
const code = typeof err === "object" ? String(err.code) : null
return code === "40001" || code === "40P01"
}
/**
* Wraps some work within a transactional block. If the service already has
* a transaction manager attached this will be reused, otherwise a new
* transaction manager is created.
* @param {function} work - the transactional work to be done
* @param {string} isolation - the isolation level to be used for the work.
* @return {any} the result of the transactional work
*/
async atomicPhase_(
work,
isolationOrErrorHandler,
maybeErrorHandlerOrDontFail
) {
let errorHandler = maybeErrorHandlerOrDontFail
let isolation = isolationOrErrorHandler
let dontFail = false
if (typeof isolationOrErrorHandler === "function") {
isolation = null
errorHandler = isolationOrErrorHandler
dontFail = !!maybeErrorHandlerOrDontFail
}
if (this.transactionManager_) {
const doWork = async (m) => {
this.manager_ = m
this.transactionManager_ = m
try {
const result = await work(m)
return result
} catch (error) {
if (errorHandler) {
const queryRunner = this.transactionManager_.queryRunner
if (queryRunner.isTransactionActive) {
await queryRunner.rollbackTransaction()
}
await errorHandler(error)
}
throw error
}
}
return doWork(this.transactionManager_)
} else {
const temp = this.manager_
const doWork = async (m) => {
this.manager_ = m
this.transactionManager_ = m
try {
const result = await work(m)
this.manager_ = temp
this.transactionManager_ = undefined
return result
} catch (error) {
this.manager_ = temp
this.transactionManager_ = undefined
throw error
}
}
if (isolation) {
let result
try {
result = await this.manager_.transaction(isolation, (m) => doWork(m))
return result
} catch (error) {
if (this.shouldRetryTransaction(error)) {
return this.manager_.transaction(isolation, (m) => doWork(m))
} else {
if (errorHandler) {
await errorHandler(error)
}
throw error
}
}
}
try {
return await this.manager_.transaction((m) => doWork(m))
} catch (error) {
if (errorHandler) {
const result = await errorHandler(error)
if (dontFail) {
return result
}
}
throw error
}
}
}
/**
* Dedicated method to set metadata.
* @param {string} obj - the entity to apply metadata to.
* @param {object} metadata - the metadata to set
* @return {Promise} resolves to the updated result.
*/
setMetadata_(obj, metadata) {
return setMetadata(obj, metadata)
}
/**
* Adds a decorator to a service. The decorator must be a function and should
* return a decorated object.
* @param {function} fn - the decorator to add to the service
*/
addDecorator(fn) {
if (typeof fn !== "function") {
throw Error("Decorators must be of type function")
}
this.decorators_.push(fn)
}
/**
* Runs the decorators registered on the service. The decorators are run in
* the order they have been registered in. Failing decorators will be skipped
* in order to ensure deliverability in spite of breaking code.
* @param {object} obj - the object to decorate.
* @return {object} the decorated object.
*/
runDecorators_(obj, fields = [], expandFields = []) {
return this.decorators_.reduce(async (acc, next) => {
return acc.then((res) => next(res, fields, expandFields)).catch(() => acc)
}, Promise.resolve(obj))
}
}
@@ -1,28 +0,0 @@
import BaseService from "./base-service"
/**
* Interface for file connectors
* @interface
* @deprecated use AbstractFileService from @medusajs/medusa instead
*/
class BaseFileService extends BaseService {
static _isFileService = true
static isFileService(obj) {
return obj?.constructor?._isFileService
}
constructor() {
super()
}
upload() {
throw Error("upload must be overridden by the child class")
}
delete() {
throw Error("delete must be overridden by the child class")
}
}
export default BaseFileService
@@ -1,117 +0,0 @@
import BaseService from "./base-service"
/**
* The interface that all fulfillment services must inherit from. The intercace
* provides the necessary methods for creating, authorizing and managing
* fulfillment orders.
* @interface
* @deprecated use AbstractFulfillmentService from @medusajs/medusa instead
*/
class BaseFulfillmentService extends BaseService {
static _isFulfillmentService = true
static isFulfillmentService(obj) {
return obj?.constructor?._isFulfillmentService
}
constructor() {
super()
}
getIdentifier() {
return this.constructor.identifier
}
/**
* Called before a shipping option is created in Admin. The method should
* return all of the options that the fulfillment provider can be used with,
* and it is here the distinction between different shipping options are
* enforced. For example, a fulfillment provider may offer Standard Shipping
* and Express Shipping as fulfillment options, it is up to the store operator
* to create shipping options in Medusa that can be chosen between by the
* customer.
*/
getFulfillmentOptions() {
throw Error("getFulfillmentOptions must be overridden by the child class")
}
/**
* Called before a shipping method is set on a cart to ensure that the data
* sent with the shipping method is valid. The data object may contain extra
* data about the shipment such as an id of a drop point. It is up to the
* fulfillment provider to enforce that the correct data is being sent
* through.
* @param {object} optionData - the data to validate
* @param {object} data - the data to validate
* @param {object | undefined} cart - the cart to which the shipping method will be applied
* @return {object} the data to populate `cart.shipping_methods.$.data` this
* is usually important for future actions like generating shipping labels
*/
validateFulfillmentData(optionData, data, cart) {
throw Error("validateFulfillmentData must be overridden by the child class")
}
/**
* Called before a shipping option is created in Admin. Use this to ensure
* that a fulfillment option does in fact exist.
*/
validateOption(data) {
throw Error("validateOption must be overridden by the child class")
}
canCalculate(data) {
throw Error("canCalculate must be overridden by the child class")
}
/**
* Used to calculate a price for a given shipping option.
*/
calculatePrice(optionData, data, cart) {
throw Error("calculatePrice must be overridden by the child class")
}
createFulfillment(data, items, order, fulfillment) {
throw Error("createFulfillment must be overridden by the child class")
}
cancelFulfillment(fulfillment) {
throw Error("cancelFulfillment must be overridden by the child class")
}
/**
* Used to retrieve documents associated with a fulfillment.
* Will default to returning no documents.
*/
getFulfillmentDocuments(data) {
return []
}
/**
* Used to create a return order. Should return the data necessary for future
* operations on the return; in particular the data may be used to receive
* documents attached to the return.
*/
createReturn(fromData) {
throw Error("createReturn must be overridden by the child class")
}
/**
* Used to retrieve documents related to a return order.
*/
getReturnDocuments(data) {
return []
}
/**
* Used to retrieve documents related to a shipment.
*/
getShipmentDocuments(data) {
return []
}
retrieveDocuments(fulfillmentData, documentType) {
throw Error("retrieveDocuments must be overridden by the child class")
}
}
export default BaseFulfillmentService
-7
View File
@@ -1,7 +0,0 @@
export { default as BaseService } from "./base-service"
export { default as PaymentService } from "./payment-service"
export { default as FulfillmentService } from "./fulfillment-service"
export { default as FileService } from "./file-service"
export { default as NotificationService } from "./notification-service"
export { default as OauthService } from "./oauth-service"
export { default as SearchService } from "./search-service"
@@ -1,35 +0,0 @@
import BaseService from "./base-service"
/**
* Interface for Notification Providers
* @interface
* @deprecated use AbstractNotificationService from @medusajs/medusa instead
*/
class BaseNotificationService extends BaseService {
static _isNotificationService = true
static isNotificationService(obj) {
return obj?.constructor?._isNotificationService
}
constructor() {
super()
}
getIdentifier() {
return this.constructor.identifier
}
/**
* Used to retrieve documents related to a shipment.
*/
sendNotification(event, data) {
throw new Error("Must be overridden by child")
}
resendNotification(notification, config = {}) {
throw new Error("Must be overridden by child")
}
}
export default BaseNotificationService
@@ -1,31 +0,0 @@
import BaseService from "./base-service"
/**
* Interface for file connectors
* @interface
*/
class BaseOauthService extends BaseService {
static _isOauthService = true
static isOauthService(obj) {
return obj?.constructor?._isOauthService
}
constructor() {
super()
}
generateToken() {
throw Error("generateToken must be overridden by the child class")
}
refreshToken() {
throw Error("refreshToken must be overridden by the child class")
}
destroyToken() {
throw Error("destroyToken must be overridden by the child class")
}
}
export default BaseOauthService
@@ -1,85 +0,0 @@
import BaseService from "./base-service"
/**
* The interface that all payment services must inherit from. The intercace
* provides the necessary methods for creating, authorizing and managing
* payments.
* @interface
* @deprecated use AbstractPaymentProcessor from @medusajs/medusa instead
*/
class BasePaymentService extends BaseService {
static _isPaymentService = true
static isPaymentService(obj) {
return obj?.constructor?._isPaymentService
}
constructor() {
super()
}
getIdentifier() {
return this.constructor.identifier
}
/**
* Used to create a payment to be processed with the service's payment gateway.
* @param cart {object} - the cart that the payment should cover.
* @return {Promise<{object}>} - returns a promise that resolves to an object
* containing the payment data. This data will be saved to the cart for later
* use.
*/
createPayment(cart) {
throw Error("createPayment must be overridden by the child class")
}
/**
* Used to retrieve a payment.
* @param cart {object} - the cart whose payment should be retrieved.
* @return {Promise<{object}>} - returns a promise that resolves to the
* payment object as stored with the provider.
*/
retrievePayment(cart) {
throw Error("getPayment must be overridden by the child class")
}
/**
* Used to update a payment. This method is called when the cart is updated.
* @param cart {object} - the cart whose payment should be updated.
* @return {Promise<{object}>} - returns a promise that resolves to the
* payment object as stored with the provider.
*/
updatePayment(cart) {
throw Error("updatePayment must be overridden by the child class")
}
getStatus() {
throw Error("getStatus must be overridden by the child class")
}
authorizePayment() {
throw Error("authorizePayment must be overridden by the child class")
}
capturePayment() {
throw Error("capturePayment must be overridden by the child class")
}
refundPayment() {
throw Error("refundPayment must be overridden by the child class")
}
deletePayment() {
throw Error("deletePayment must be overridden by the child class")
}
/**
* If the payment provider can save a payment method this function will
* retrieve them.
*/
retrieveSavedMethods(customer) {
return Promise.resolve([])
}
}
export default BasePaymentService
@@ -1,107 +0,0 @@
import BaseService from "./base-service"
/**
* The interface that all search services must implement.
* @interface
* @deprecated use AbstractSearchService from @medusajs/utils instead
*/
class SearchService extends BaseService {
static _isSearchService = true
static isSearchService(obj) {
return obj?.constructor?._isSearchService
}
constructor() {
super()
}
get options() {
return this.options_ ?? {}
}
/**
* Used to create an index
* @param indexName {string} - the index name
* @param [options] {string} - the index name
* @return {Promise<{object}>} - returns response from search engine provider
*/
createIndex(indexName, options) {
throw Error("createIndex must be overridden by a child class")
}
/**
* Used to get an index
* @param indexName {string} - the index name.
* @return {Promise<{object}>} - returns response from search engine provider
*/
getIndex(indexName) {
throw Error("getIndex must be overridden by a child class")
}
/**
* Used to index documents by the search engine provider
* @param indexName {string} - the index name
* @param documents {Array.<Object>} - documents array to be indexed
* @param type {string} - type of documents to be added (e.g: products, regions, orders, etc)
* @return {Promise<{object}>} - returns response from search engine provider
*/
addDocuments(indexName, documents, type) {
throw Error("addDocuments must be overridden by a child class")
}
/**
* Used to replace documents
* @param indexName {string} - the index name.
* @param documents {Object} - array of document objects that will replace existing documents
* @param type {Array.<Object>} - type of documents to be replaced (e.g: products, regions, orders, etc)
* @return {Promise<{object}>} - returns response from search engine provider
*/
replaceDocuments(indexName, documents, type) {
throw Error("updateDocument must be overridden by a child class")
}
/**
* Used to delete document
* @param indexName {string} - the index name
* @param document_id {string} - the id of the document
* @return {Promise<{object}>} - returns response from search engine provider
*/
deleteDocument(indexName, document_id) {
throw Error("deleteDocument must be overridden by a child class")
}
/**
* Used to delete all documents
* @param indexName {string} - the index name
* @return {Promise<{object}>} - returns response from search engine provider
*/
deleteAllDocuments(indexName) {
throw Error("deleteAllDocuments must be overridden by a child class")
}
/**
* Used to search for a document in an index
* @param indexName {string} - the index name
* @param query {string} - the search query
* @param options {{ paginationOptions: { limit: number, offset: number }, filter: any, additionalOptions: any}}
* - any options passed to the request object other than the query and indexName
* - additionalOptions contain any provider specific options
* @return {Promise<{ hits: any[]; [k: string]: any; }>} returns response from search engine provider
*/
search(indexName, query, options) {
throw Error("search must be overridden by a child class")
}
/**
* Used to update the settings of an index
* @param indexName {string} - the index name
* @param settings {object} - settings object
* @return {Promise<{object}>} - returns response from search engine provider
*/
updateSettings(indexName, settings) {
throw Error("updateSettings must be overridden by a child class")
}
}
export default SearchService
-28
View File
@@ -1,28 +0,0 @@
{
"compilerOptions": {
"lib": ["es2021"],
"target": "es2021",
"outDir": "./dist",
"esModuleInterop": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true // to use ES5 specific tooling
},
"include": ["./src/**/*", "index.d.ts"],
"exclude": [
"./dist/**/*",
"./src/**/__tests__",
"./src/**/__mocks__",
"node_modules"
]
}
-1
View File
@@ -1 +0,0 @@
declare module "medusa-interfaces"
-4
View File
@@ -31,7 +31,6 @@
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"jest": "^25.5.4", "jest": "^25.5.4",
"medusa-interfaces": "^1.3.9",
"medusa-test-utils": "^1.1.44", "medusa-test-utils": "^1.1.44",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",
"supertest": "^4.0.2", "supertest": "^4.0.2",
@@ -45,9 +44,6 @@
"serve": "node dist/app.js", "serve": "node dist/app.js",
"test": "jest --silent --bail --maxWorkers=50% --forceExit" "test": "jest --silent --bail --maxWorkers=50% --forceExit"
}, },
"peerDependencies": {
"medusa-interfaces": "^1.3.7"
},
"dependencies": { "dependencies": {
"@medusajs/admin-sdk": "0.0.1", "@medusajs/admin-sdk": "0.0.1",
"@medusajs/core-flows": "^0.0.9", "@medusajs/core-flows": "^0.0.9",
@@ -14,17 +14,17 @@ import {
UpdateServiceZoneDTO, UpdateServiceZoneDTO,
} from "@medusajs/types" } from "@medusajs/types"
import { import {
arrayDifference,
EmitEvents, EmitEvents,
FulfillmentUtils, FulfillmentUtils,
getSetDifference,
InjectManager, InjectManager,
InjectTransactionManager, InjectTransactionManager,
isString,
MedusaContext, MedusaContext,
MedusaError, MedusaError,
Modules, Modules,
ModulesSdkUtils, ModulesSdkUtils,
arrayDifference,
getSetDifference,
isString,
promiseAll, promiseAll,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
@@ -49,6 +49,9 @@ export const LocationFulfillmentSet: ModuleJoinerConfig = {
}, },
{ {
serviceName: Modules.FULFILLMENT, serviceName: Modules.FULFILLMENT,
fieldAlias: {
location: "locations_link.location",
},
relationship: { relationship: {
serviceName: LINKS.LocationFulfillmentSet, serviceName: LINKS.LocationFulfillmentSet,
primaryKey: "fulfillment_set_id", primaryKey: "fulfillment_set_id",
@@ -12,3 +12,4 @@ export * from "./readonly"
export * from "./region-payment-provider" export * from "./region-payment-provider"
export * from "./sales-channel-location" export * from "./sales-channel-location"
export * from "./shipping-option-price-set" export * from "./shipping-option-price-set"
export * from "./order-fulfillment"
@@ -0,0 +1,63 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
import { LINKS } from "@medusajs/utils"
export const OrderFulfillment: ModuleJoinerConfig = {
serviceName: LINKS.OrderFulfillment,
isLink: true,
databaseConfig: {
tableName: "order_fulfillment",
idPrefix: "orderful",
},
alias: [
{
name: ["order_fulfillment", "order_fulfillments"],
args: {
entity: "LinkOrderFulfillment",
},
},
],
primaryKeys: ["id", "order_id", "fulfillment_id"],
relationships: [
{
serviceName: Modules.ORDER,
primaryKey: "id",
foreignKey: "order_id",
alias: "order",
},
{
serviceName: Modules.FULFILLMENT,
primaryKey: "id",
foreignKey: "fulfillment_id",
alias: "fulfillments",
args: {
// TODO: We are not suppose to know the module implementation here, wait for later to think about inferring it
methodSuffix: "Fulfillments",
},
},
],
extends: [
{
serviceName: Modules.ORDER,
fieldAlias: {
fulfillments: "fulfillment_link.fulfillments",
},
relationship: {
serviceName: LINKS.OrderFulfillment,
primaryKey: "order_id",
foreignKey: "id",
alias: "fulfillment_link",
isList: true,
},
},
{
serviceName: Modules.FULFILLMENT,
relationship: {
serviceName: LINKS.OrderFulfillment,
primaryKey: "fulfillment_id",
foreignKey: "id",
alias: "order_link",
},
},
],
}
@@ -2214,7 +2214,7 @@ export default class OrderModuleService<
if (!isString(data.shipping_method)) { if (!isString(data.shipping_method)) {
const methods = await this.createShippingMethods( const methods = await this.createShippingMethods(
data.order_id, data.order_id,
data.shipping_method as any, [{ order_id: data.order_id, ...data.shipping_method }],
sharedContext sharedContext
) )
shippingMethodId = methods[0].id shippingMethodId = methods[0].id
+9 -36
View File
@@ -870,7 +870,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/cli@npm:^7.12.10, @babel/cli@npm:^7.14.3, @babel/cli@npm:^7.7.5": "@babel/cli@npm:^7.12.10, @babel/cli@npm:^7.14.3":
version: 7.24.5 version: 7.24.5
resolution: "@babel/cli@npm:7.24.5" resolution: "@babel/cli@npm:7.24.5"
dependencies: dependencies:
@@ -1366,7 +1366,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-proposal-class-properties@npm:^7.10.4, @babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.7.4": "@babel/plugin-proposal-class-properties@npm:^7.10.4, @babel/plugin-proposal-class-properties@npm:^7.12.1":
version: 7.18.6 version: 7.18.6
resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6"
dependencies: dependencies:
@@ -1875,7 +1875,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-transform-classes@npm:^7.10.4, @babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.24.5, @babel/plugin-transform-classes@npm:^7.9.5": "@babel/plugin-transform-classes@npm:^7.10.4, @babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.24.5":
version: 7.24.5 version: 7.24.5
resolution: "@babel/plugin-transform-classes@npm:7.24.5" resolution: "@babel/plugin-transform-classes@npm:7.24.5"
dependencies: dependencies:
@@ -2012,7 +2012,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-transform-instanceof@npm:^7.10.4, @babel/plugin-transform-instanceof@npm:^7.12.1, @babel/plugin-transform-instanceof@npm:^7.8.3": "@babel/plugin-transform-instanceof@npm:^7.10.4, @babel/plugin-transform-instanceof@npm:^7.12.1":
version: 7.24.1 version: 7.24.1
resolution: "@babel/plugin-transform-instanceof@npm:7.24.1" resolution: "@babel/plugin-transform-instanceof@npm:7.24.1"
dependencies: dependencies:
@@ -2360,7 +2360,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-transform-runtime@npm:^7.11.5, @babel/plugin-transform-runtime@npm:^7.12.1, @babel/plugin-transform-runtime@npm:^7.7.6": "@babel/plugin-transform-runtime@npm:^7.11.5, @babel/plugin-transform-runtime@npm:^7.12.1":
version: 7.24.3 version: 7.24.3
resolution: "@babel/plugin-transform-runtime@npm:7.24.3" resolution: "@babel/plugin-transform-runtime@npm:7.24.3"
dependencies: dependencies:
@@ -2571,7 +2571,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/preset-env@npm:^7.11.5, @babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.12.7, @babel/preset-env@npm:^7.23.2, @babel/preset-env@npm:^7.7.5": "@babel/preset-env@npm:^7.11.5, @babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.12.7, @babel/preset-env@npm:^7.23.2":
version: 7.24.5 version: 7.24.5
resolution: "@babel/preset-env@npm:7.24.5" resolution: "@babel/preset-env@npm:7.24.5"
dependencies: dependencies:
@@ -2732,7 +2732,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/preset-typescript@npm:^7.13.0, @babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.0, @babel/preset-typescript@npm:^7.23.0": "@babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.0, @babel/preset-typescript@npm:^7.23.0":
version: 7.24.1 version: 7.24.1
resolution: "@babel/preset-typescript@npm:7.24.1" resolution: "@babel/preset-typescript@npm:7.24.1"
dependencies: dependencies:
@@ -2769,7 +2769,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2, @babel/runtime@npm:^7.9.6": "@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.10, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.8, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
version: 7.24.5 version: 7.24.5
resolution: "@babel/runtime@npm:7.24.5" resolution: "@babel/runtime@npm:7.24.5"
dependencies: dependencies:
@@ -5672,7 +5672,6 @@ __metadata:
jsonwebtoken: ^9.0.0 jsonwebtoken: ^9.0.0
lodash: ^4.17.21 lodash: ^4.17.21
medusa-core-utils: ^1.2.2 medusa-core-utils: ^1.2.2
medusa-interfaces: ^1.3.9
medusa-telemetry: ^0.0.17 medusa-telemetry: ^0.0.17
medusa-test-utils: ^1.1.44 medusa-test-utils: ^1.1.44
morgan: ^1.9.1 morgan: ^1.9.1
@@ -5693,8 +5692,6 @@ __metadata:
typescript: ^4.4.4 typescript: ^4.4.4
uuid: ^9.0.0 uuid: ^9.0.0
zod: 3.22.4 zod: 3.22.4
peerDependencies:
medusa-interfaces: ^1.3.7
bin: bin:
medusa: ./cli.js medusa: ./cli.js
languageName: unknown languageName: unknown
@@ -20250,7 +20247,6 @@ __metadata:
form-data: ^4.0.0 form-data: ^4.0.0
jest: ^26.6.3 jest: ^26.6.3
jest-environment-node: 26.6.2 jest-environment-node: 26.6.2
medusa-interfaces: "workspace:*"
pg: ^8.11.0 pg: ^8.11.0
typeorm: ^0.3.16 typeorm: ^0.3.16
languageName: unknown languageName: unknown
@@ -20290,7 +20286,6 @@ __metadata:
faker: ^5.5.3 faker: ^5.5.3
jest: ^26.6.3 jest: ^26.6.3
jest-environment-node: 26.6.2 jest-environment-node: 26.6.2
medusa-interfaces: "workspace:*"
medusa-test-utils: "workspace:*" medusa-test-utils: "workspace:*"
pg: ^8.11.0 pg: ^8.11.0
typeorm: ^0.3.16 typeorm: ^0.3.16
@@ -24207,7 +24202,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"medusa-core-utils@^1.2.0, medusa-core-utils@^1.2.1, medusa-core-utils@^1.2.2, medusa-core-utils@workspace:packages/medusa-core-utils": "medusa-core-utils@^1.2.0, medusa-core-utils@^1.2.2, medusa-core-utils@workspace:packages/medusa-core-utils":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "medusa-core-utils@workspace:packages/medusa-core-utils" resolution: "medusa-core-utils@workspace:packages/medusa-core-utils"
dependencies: dependencies:
@@ -24246,28 +24241,6 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"medusa-interfaces@^1.3.9, medusa-interfaces@workspace:*, medusa-interfaces@workspace:packages/medusa-interfaces":
version: 0.0.0-use.local
resolution: "medusa-interfaces@workspace:packages/medusa-interfaces"
dependencies:
"@babel/cli": ^7.7.5
"@babel/core": ^7.7.5
"@babel/plugin-proposal-class-properties": ^7.7.4
"@babel/plugin-transform-classes": ^7.9.5
"@babel/plugin-transform-instanceof": ^7.8.3
"@babel/plugin-transform-runtime": ^7.7.6
"@babel/preset-env": ^7.7.5
"@babel/preset-typescript": ^7.13.0
"@babel/runtime": ^7.9.6
cross-env: ^5.2.1
jest: ^25.5.4
medusa-core-utils: ^1.2.1
medusa-test-utils: ^1.1.42
rimraf: ^5.0.1
typescript: ^4.4.4
languageName: unknown
linkType: soft
"medusa-react@npm:latest": "medusa-react@npm:latest":
version: 9.0.17 version: 9.0.17
resolution: "medusa-react@npm:9.0.17" resolution: "medusa-react@npm:9.0.17"