Merge branch 'develop' into fix/2152
This commit is contained in:
@@ -28,7 +28,6 @@
|
||||
"@medusajs/utils": "workspace:^",
|
||||
"@medusajs/workflow-engine-inmemory": "workspace:*",
|
||||
"faker": "^5.5.3",
|
||||
"medusa-interfaces": "workspace:*",
|
||||
"pg": "^8.11.0",
|
||||
"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",
|
||||
},*/
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -31,7 +31,6 @@
|
||||
"@medusajs/utils": "workspace:^",
|
||||
"@medusajs/workflow-engine-inmemory": "workspace:*",
|
||||
"faker": "^5.5.3",
|
||||
"medusa-interfaces": "workspace:*",
|
||||
"medusa-test-utils": "workspace:*",
|
||||
"pg": "^8.11.0",
|
||||
"typeorm": "^0.3.16"
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
},
|
||||
"actions": {
|
||||
"save": "Save",
|
||||
"select": "Select",
|
||||
"saveAsDraft": "Save as draft",
|
||||
"publish": "Publish",
|
||||
"create": "Create",
|
||||
@@ -653,8 +654,8 @@
|
||||
}
|
||||
},
|
||||
"shipping": {
|
||||
"title": "Location & Shipping",
|
||||
"domain": "Location & Shipping",
|
||||
"title": "Locations & 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.",
|
||||
"createLocation": "Create location",
|
||||
"createLocationDetailsHint": "Specify the details of the location.",
|
||||
@@ -714,10 +715,12 @@
|
||||
"edit": {
|
||||
"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.",
|
||||
"toast": {
|
||||
"delete": "Zone \"{{name}}\" deleted successfully."
|
||||
},
|
||||
"manageAreas": "Manage areas",
|
||||
"editPrices": "Edit prices",
|
||||
"editOption": "Edit option",
|
||||
"optionsLength_one": "shipping option",
|
||||
@@ -745,8 +748,8 @@
|
||||
"allocation": "Shipping amount",
|
||||
"fixed": "Fixed",
|
||||
"fixedDescription": "Shipping option's price is always the same amount.",
|
||||
"enable": "Enable in store",
|
||||
"enableDescription": "Enable or disable the shipping option visiblity in store",
|
||||
"enable": "Show publicly",
|
||||
"enableDescription": "When disabled, the shipping option can only be applied by admins.",
|
||||
"calculated": "Calculated",
|
||||
"calculatedDescription": "Shipping option's price is calculated by the fulfillment provider.",
|
||||
"profile": "Shipping profile"
|
||||
|
||||
@@ -41,7 +41,7 @@ export const useStockLocation = (
|
||||
) => {
|
||||
const { data, ...rest } = useQuery({
|
||||
queryFn: () => client.stockLocations.retrieve(id, query),
|
||||
queryKey: stockLocationsQueryKeys.detail(id, query),
|
||||
queryKey: stockLocationsQueryKeys.details(),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -744,6 +744,13 @@ export const RouteMap: RouteObject[] = [
|
||||
"../../v2-routes/shipping/service-zone-edit"
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "edit-areas",
|
||||
lazy: () =>
|
||||
import(
|
||||
"../../v2-routes/shipping/service-zone-areas-edit"
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "shipping-option",
|
||||
children: [
|
||||
@@ -764,6 +771,13 @@ export const RouteMap: RouteObject[] = [
|
||||
"../../v2-routes/shipping/shipping-option-edit"
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "edit-pricing",
|
||||
lazy: () =>
|
||||
import(
|
||||
"../../v2-routes/shipping/shipping-options-edit-pricing"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
+9
-8
@@ -156,7 +156,7 @@ function ShippingOption({
|
||||
{
|
||||
label: t("shipping.serviceZone.editPrices"),
|
||||
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"),
|
||||
@@ -199,7 +199,7 @@ function ServiceZoneOptions({
|
||||
{t("shipping.serviceZone.shippingOptions")}
|
||||
</span>
|
||||
<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"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
@@ -231,7 +231,7 @@ function ServiceZoneOptions({
|
||||
{t("shipping.serviceZone.returnOptions")}
|
||||
</span>
|
||||
<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"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
@@ -313,6 +313,7 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
|
||||
.filter((g) => g.type === "country")
|
||||
.map((g) => g.country_code)
|
||||
.map((code) => staticCountries.find((c) => c.iso_2 === code))
|
||||
.sort((c1, c2) => c1.name.localeCompare(c2.name))
|
||||
}, zone.geo_zones)
|
||||
|
||||
const [shippingOptionsCount, returnOptionsCount] = useMemo(() => {
|
||||
@@ -385,16 +386,16 @@ function ServiceZone({ zone, locationId, fulfillmentSetId }: ServiceZoneProps) {
|
||||
groups={[
|
||||
{
|
||||
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"),
|
||||
icon: <PencilSquare />,
|
||||
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"),
|
||||
icon: <Trash />,
|
||||
|
||||
+1
-1
@@ -181,7 +181,7 @@ function Location(props: LocationProps) {
|
||||
]}
|
||||
/>
|
||||
<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}`)}
|
||||
variant="transparent"
|
||||
>
|
||||
|
||||
+344
@@ -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>[]
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./edit-service-zone-areas-form"
|
||||
@@ -0,0 +1 @@
|
||||
export { ServiceZoneAreasEdit as Component } from "./service-zone-areas-edit"
|
||||
+45
@@ -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>
|
||||
)
|
||||
}
|
||||
+17
-9
@@ -16,10 +16,11 @@ import {
|
||||
IconButton,
|
||||
Input,
|
||||
Text,
|
||||
toast,
|
||||
} from "@medusajs/ui"
|
||||
import { FulfillmentSetDTO, RegionCountryDTO, RegionDTO } from "@medusajs/types"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Map, XMark, XMarkMini } from "@medusajs/icons"
|
||||
import { XMarkMini } from "@medusajs/icons"
|
||||
|
||||
import {
|
||||
RouteFocusModal,
|
||||
@@ -87,13 +88,20 @@ export function CreateServiceZoneForm({
|
||||
useCreateServiceZone(locationId, fulfillmentSet.id)
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
await createServiceZone({
|
||||
name: data.name,
|
||||
geo_zones: data.countries.map((iso2) => ({
|
||||
country_code: iso2,
|
||||
type: "country",
|
||||
})),
|
||||
})
|
||||
try {
|
||||
await createServiceZone({
|
||||
name: data.name,
|
||||
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()
|
||||
})
|
||||
@@ -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">
|
||||
<SplitView open={open} onOpenChange={handleOpenChange}>
|
||||
<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">
|
||||
{t("shipping.fulfillmentSet.create.title", {
|
||||
fulfillmentSet: fulfillmentSet.name,
|
||||
|
||||
@@ -6,7 +6,9 @@ import { StockLocationRes } from "../../../types/api-responses"
|
||||
import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations"
|
||||
|
||||
const fulfillmentSetCreateQuery = (id: string) => ({
|
||||
queryKey: stockLocationsQueryKeys.detail(id),
|
||||
queryKey: stockLocationsQueryKeys.detail(id, {
|
||||
fields: "*fulfillment_sets",
|
||||
}),
|
||||
queryFn: async () =>
|
||||
client.stockLocations.retrieve(id, {
|
||||
fields: "*fulfillment_sets",
|
||||
|
||||
+5
-5
@@ -45,7 +45,7 @@ type StepStatus = {
|
||||
[key in Tab]: ProgressStatus
|
||||
}
|
||||
|
||||
const CreateServiceZoneSchema = zod.object({
|
||||
const CreateShippingOptionSchema = zod.object({
|
||||
name: zod.string().min(1),
|
||||
price_type: zod.nativeEnum(ShippingAllocation),
|
||||
enabled_in_store: zod.boolean().optional(),
|
||||
@@ -55,7 +55,7 @@ const CreateServiceZoneSchema = zod.object({
|
||||
currency_prices: zod.record(zod.string(), zod.string().optional()),
|
||||
})
|
||||
|
||||
type CreateServiceZoneFormProps = {
|
||||
type CreateShippingOptionFormProps = {
|
||||
zone: ServiceZoneDTO
|
||||
isReturn?: boolean
|
||||
}
|
||||
@@ -63,7 +63,7 @@ type CreateServiceZoneFormProps = {
|
||||
export function CreateShippingOptionsForm({
|
||||
zone,
|
||||
isReturn,
|
||||
}: CreateServiceZoneFormProps) {
|
||||
}: CreateShippingOptionFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
const [tab, setTab] = React.useState<Tab>(Tab.DETAILS)
|
||||
@@ -77,7 +77,7 @@ export function CreateShippingOptionsForm({
|
||||
fields: "id,currency_code",
|
||||
})
|
||||
|
||||
const form = useForm<zod.infer<typeof CreateServiceZoneSchema>>({
|
||||
const form = useForm<zod.infer<typeof CreateShippingOptionSchema>>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
price_type: ShippingAllocation.FlatRate,
|
||||
@@ -87,7 +87,7 @@ export function CreateShippingOptionsForm({
|
||||
region_prices: {},
|
||||
currency_prices: {},
|
||||
},
|
||||
resolver: zodResolver(CreateServiceZoneSchema),
|
||||
resolver: zodResolver(CreateShippingOptionSchema),
|
||||
})
|
||||
|
||||
const isCalculatedPriceType =
|
||||
|
||||
+4
-1
@@ -6,7 +6,10 @@ import { StockLocationRes } from "../../../types/api-responses"
|
||||
import { stockLocationsQueryKeys } from "../../../hooks/api/stock-locations"
|
||||
|
||||
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 () =>
|
||||
client.stockLocations.retrieve(id, {
|
||||
fields:
|
||||
|
||||
+319
@@ -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
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./edit-shipping-options-pricing-form.tsx"
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { ShippingOptionsEditPricing as Component } from "./shipping-options-edit-pricing"
|
||||
+30
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -52,13 +52,13 @@ git checkout package.json; npm install --force
|
||||
You can prevent the automatic dependencies scan and instead specify a list of
|
||||
packages you want to link by using the `--packages` option:
|
||||
|
||||
`medusa-dev --packages @medusajs/medusa medusa-interfaces`
|
||||
`medusa-dev --packages @medusajs/medusa`
|
||||
|
||||
#### `--scan-once`
|
||||
|
||||
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
|
||||
code.
|
||||
code.
|
||||
|
||||
#### `--quiet`
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./steps/remove-remote-links"
|
||||
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,
|
||||
OrderWorkflowDTO,
|
||||
ShippingTaxLineDTO,
|
||||
TaxCalculationContext,
|
||||
TaxableItemDTO,
|
||||
TaxableShippingDTO,
|
||||
TaxCalculationContext,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
order: OrderWorkflowDTO
|
||||
@@ -104,8 +104,8 @@ export const getOrderItemTaxLinesStep = createStep(
|
||||
async (data: StepInput, { container }) => {
|
||||
const {
|
||||
order,
|
||||
items,
|
||||
shipping_methods: shippingMethods,
|
||||
items = [],
|
||||
shipping_methods: shippingMethods = [],
|
||||
force_tax_calculation: forceTaxCalculation = false,
|
||||
} = data
|
||||
const taxService = container.resolve<ITaxModuleService>(
|
||||
@@ -123,15 +123,19 @@ export const getOrderItemTaxLinesStep = createStep(
|
||||
return new StepResponse(stepResponseData)
|
||||
}
|
||||
|
||||
stepResponseData.lineItemTaxLines = (await taxService.getTaxLines(
|
||||
normalizeLineItemsForTax(order, items),
|
||||
taxContext
|
||||
)) as ItemTaxLineDTO[]
|
||||
if (items.length) {
|
||||
stepResponseData.lineItemTaxLines = (await taxService.getTaxLines(
|
||||
normalizeLineItemsForTax(order, items),
|
||||
taxContext
|
||||
)) as ItemTaxLineDTO[]
|
||||
}
|
||||
|
||||
stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines(
|
||||
normalizeLineItemsForShipping(order, shippingMethods),
|
||||
taxContext
|
||||
)) as ShippingTaxLineDTO[]
|
||||
if (shippingMethods.length) {
|
||||
stepResponseData.shippingMethodsTaxLines = (await taxService.getTaxLines(
|
||||
normalizeLineItemsForShipping(order, shippingMethods),
|
||||
taxContext
|
||||
)) as ShippingTaxLineDTO[]
|
||||
}
|
||||
|
||||
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 "./update-tax-lines"
|
||||
export * from "./create-return"
|
||||
|
||||
@@ -14,7 +14,7 @@ export type DeleteEntityInput = {
|
||||
}
|
||||
export type RestoreEntityInput = DeleteEntityInput
|
||||
|
||||
type LinkDefinition = {
|
||||
export type LinkDefinition = {
|
||||
[moduleName: string]: {
|
||||
[fieldName: string]: string
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from "./rule"
|
||||
export * from "./batch"
|
||||
export * from "./config-module"
|
||||
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
|
||||
}
|
||||
)[]
|
||||
|
||||
/**
|
||||
* The shipping option pricing
|
||||
*/
|
||||
prices: (
|
||||
| { currency_code: string; amount: number; id?: string }
|
||||
| { region_id: string; amount: number; id?: string }
|
||||
)[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,19 @@ import { BaseFilterable } from "../dal"
|
||||
import { OperatorMap } from "../dal/utils"
|
||||
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 = {
|
||||
total: BigNumberValue
|
||||
subtotal: BigNumberValue
|
||||
@@ -1187,7 +1200,7 @@ export interface OrderChangeActionDTO {
|
||||
/**
|
||||
* The action of the order change action
|
||||
*/
|
||||
action: string
|
||||
action: ChangeActionType
|
||||
/**
|
||||
* The details of the order change action
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BigNumberInput } from "../totals"
|
||||
import {
|
||||
ChangeActionType,
|
||||
OrderItemDTO,
|
||||
OrderLineItemDTO,
|
||||
OrderReturnReasonDTO,
|
||||
@@ -300,7 +301,7 @@ export interface CreateOrderChangeActionDTO {
|
||||
version?: number
|
||||
reference?: string
|
||||
reference_id?: string
|
||||
action: string
|
||||
action: ChangeActionType
|
||||
internal_note?: string
|
||||
amount?: BigNumberInput
|
||||
details?: Record<string, unknown>
|
||||
|
||||
@@ -9,3 +9,4 @@ export * as ProductCategoryWorkflow from "./product-category"
|
||||
export * as RegionWorkflow from "./region"
|
||||
export * as ReservationWorkflow from "./reservation"
|
||||
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"
|
||||
@@ -80,4 +80,10 @@ export const LINKS = {
|
||||
Modules.PAYMENT,
|
||||
"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") {
|
||||
await unwrapInput(parentRef[key], parentRef[key])
|
||||
parentRef[key] = await unwrapInput(parentRef[key], parentRef[key])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ export type ReturnWorkflow<
|
||||
runAsStep: ({
|
||||
input,
|
||||
}: {
|
||||
input: TData
|
||||
input: TData | WorkflowData<TData>
|
||||
}) => ReturnType<StepFunction<TData, TResult>>
|
||||
run: <TDataOverride = undefined, TResultOverride = undefined>(
|
||||
...args: Parameters<
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
dist/
|
||||
node_modules/
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
declare module "medusa-interfaces"
|
||||
@@ -31,7 +31,6 @@
|
||||
"@types/multer": "^1.4.7",
|
||||
"cross-env": "^5.2.1",
|
||||
"jest": "^25.5.4",
|
||||
"medusa-interfaces": "^1.3.9",
|
||||
"medusa-test-utils": "^1.1.44",
|
||||
"rimraf": "^5.0.1",
|
||||
"supertest": "^4.0.2",
|
||||
@@ -45,9 +44,6 @@
|
||||
"serve": "node dist/app.js",
|
||||
"test": "jest --silent --bail --maxWorkers=50% --forceExit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"medusa-interfaces": "^1.3.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/admin-sdk": "0.0.1",
|
||||
"@medusajs/core-flows": "^0.0.9",
|
||||
|
||||
@@ -14,17 +14,17 @@ import {
|
||||
UpdateServiceZoneDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
arrayDifference,
|
||||
EmitEvents,
|
||||
FulfillmentUtils,
|
||||
getSetDifference,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isString,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
Modules,
|
||||
ModulesSdkUtils,
|
||||
arrayDifference,
|
||||
getSetDifference,
|
||||
isString,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
|
||||
@@ -49,6 +49,9 @@ export const LocationFulfillmentSet: ModuleJoinerConfig = {
|
||||
},
|
||||
{
|
||||
serviceName: Modules.FULFILLMENT,
|
||||
fieldAlias: {
|
||||
location: "locations_link.location",
|
||||
},
|
||||
relationship: {
|
||||
serviceName: LINKS.LocationFulfillmentSet,
|
||||
primaryKey: "fulfillment_set_id",
|
||||
|
||||
@@ -12,3 +12,4 @@ export * from "./readonly"
|
||||
export * from "./region-payment-provider"
|
||||
export * from "./sales-channel-location"
|
||||
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)) {
|
||||
const methods = await this.createShippingMethods(
|
||||
data.order_id,
|
||||
data.shipping_method as any,
|
||||
[{ order_id: data.order_id, ...data.shipping_method }],
|
||||
sharedContext
|
||||
)
|
||||
shippingMethodId = methods[0].id
|
||||
|
||||
@@ -870,7 +870,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/cli@npm:7.24.5"
|
||||
dependencies:
|
||||
@@ -1366,7 +1366,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6"
|
||||
dependencies:
|
||||
@@ -1875,7 +1875,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/plugin-transform-classes@npm:7.24.5"
|
||||
dependencies:
|
||||
@@ -2012,7 +2012,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/plugin-transform-instanceof@npm:7.24.1"
|
||||
dependencies:
|
||||
@@ -2360,7 +2360,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/plugin-transform-runtime@npm:7.24.3"
|
||||
dependencies:
|
||||
@@ -2571,7 +2571,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/preset-env@npm:7.24.5"
|
||||
dependencies:
|
||||
@@ -2732,7 +2732,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/preset-typescript@npm:7.24.1"
|
||||
dependencies:
|
||||
@@ -2769,7 +2769,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "@babel/runtime@npm:7.24.5"
|
||||
dependencies:
|
||||
@@ -5672,7 +5672,6 @@ __metadata:
|
||||
jsonwebtoken: ^9.0.0
|
||||
lodash: ^4.17.21
|
||||
medusa-core-utils: ^1.2.2
|
||||
medusa-interfaces: ^1.3.9
|
||||
medusa-telemetry: ^0.0.17
|
||||
medusa-test-utils: ^1.1.44
|
||||
morgan: ^1.9.1
|
||||
@@ -5693,8 +5692,6 @@ __metadata:
|
||||
typescript: ^4.4.4
|
||||
uuid: ^9.0.0
|
||||
zod: 3.22.4
|
||||
peerDependencies:
|
||||
medusa-interfaces: ^1.3.7
|
||||
bin:
|
||||
medusa: ./cli.js
|
||||
languageName: unknown
|
||||
@@ -20250,7 +20247,6 @@ __metadata:
|
||||
form-data: ^4.0.0
|
||||
jest: ^26.6.3
|
||||
jest-environment-node: 26.6.2
|
||||
medusa-interfaces: "workspace:*"
|
||||
pg: ^8.11.0
|
||||
typeorm: ^0.3.16
|
||||
languageName: unknown
|
||||
@@ -20290,7 +20286,6 @@ __metadata:
|
||||
faker: ^5.5.3
|
||||
jest: ^26.6.3
|
||||
jest-environment-node: 26.6.2
|
||||
medusa-interfaces: "workspace:*"
|
||||
medusa-test-utils: "workspace:*"
|
||||
pg: ^8.11.0
|
||||
typeorm: ^0.3.16
|
||||
@@ -24207,7 +24202,7 @@ __metadata:
|
||||
languageName: node
|
||||
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
|
||||
resolution: "medusa-core-utils@workspace:packages/medusa-core-utils"
|
||||
dependencies:
|
||||
@@ -24246,28 +24241,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
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":
|
||||
version: 9.0.17
|
||||
resolution: "medusa-react@npm:9.0.17"
|
||||
|
||||
Reference in New Issue
Block a user