diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index 3f9099df56..c708f88845 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -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" }, diff --git a/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts new file mode 100644 index 0000000000..858f801626 --- /dev/null +++ b/integration-tests/modules/__tests__/order/workflows/create-return.spec.ts @@ -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", + },*/ + }), + ], + }) + ) + }) + }) + }, +}) diff --git a/integration-tests/modules/package.json b/integration-tests/modules/package.json index 16f61a8f31..f4b236441c 100644 --- a/integration-tests/modules/package.json +++ b/integration-tests/modules/package.json @@ -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" diff --git a/packages/admin-next/dashboard/public/locales/en-US/translation.json b/packages/admin-next/dashboard/public/locales/en-US/translation.json index 89eb9c41db..3fb5f382ca 100644 --- a/packages/admin-next/dashboard/public/locales/en-US/translation.json +++ b/packages/admin-next/dashboard/public/locales/en-US/translation.json @@ -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" diff --git a/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx index 555f94b40b..9683d301b5 100644 --- a/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/stock-locations.tsx @@ -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, }) diff --git a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx index 4601f7a80e..87701f6484 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx @@ -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" + ), + }, ], }, ], diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx index 10ad9d22cd..1d1827a234 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/location-details/components/location-general-section/location-general-section.tsx @@ -156,7 +156,7 @@ function ShippingOption({ { label: t("shipping.serviceZone.editPrices"), icon: , - 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")} + + + + ) +} + +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( + zone.geo_zones + .map((z) => z.country_code) + .reduce((acc, v) => { + acc[v] = true + return acc + }, {}) + ) + + const form = useForm>({ + 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 ( + +
+ +
+ + + + +
+
+ + + + +
+ + {t("shipping.serviceZone.editAreasTitle", { + zone: zone.name, + })} + +
+ +
+
+ + {t("shipping.serviceZone.areas.title")} + + + {t("shipping.serviceZone.areas.description")} + +
+ +
+ {!!selectedCountries.length && ( +
+ {selectedCountries.map((c) => ( + + {c.display_name} + removeCountry(c.iso_2)} + className="text-ui-fg-subtle p-0 px-1 pt-[1px]" + variant="transparent" + > + + + + ))} + +
+ )} + {showAreasError && ( + + {t("shipping.serviceZone.areas.error")} + + )} +
+ +
+ + +
+
+
+
+
+
+ ) +} + +const columnHelper = createColumnHelper() + +const useColumns = () => { + const base = useCountryTableColumns() + + return useMemo( + () => [ + columnHelper.display({ + id: "select", + header: ({ table }) => { + return ( + + table.toggleAllPageRowsSelected(!!value) + } + /> + ) + }, + cell: ({ row }) => { + const isPreselected = !row.getCanSelect() + + return ( + row.toggleSelected(!!value)} + onClick={(e) => { + e.stopPropagation() + }} + /> + ) + }, + }), + ...base, + ], + [base] + ) as ColumnDef[] +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts new file mode 100644 index 0000000000..522cccf066 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/components/edit-region-areas-form/index.ts @@ -0,0 +1 @@ +export * from "./edit-service-zone-areas-form" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts new file mode 100644 index 0000000000..fc088b9f88 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/index.ts @@ -0,0 +1 @@ +export { ServiceZoneAreasEdit as Component } from "./service-zone-areas-edit" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx new file mode 100644 index 0000000000..0bccfa6b4a --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-areas-edit/service-zone-areas-edit.tsx @@ -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 ( + + {!isPending && zone && ( + + )} + + ) +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx index a29b5e412b..3263a0f0e4 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/components/create-service-zone-form/create-service-zone-form.tsx @@ -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({ -
+
{t("shipping.fulfillmentSet.create.title", { fulfillmentSet: fulfillmentSet.name, diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts index d390458e11..5d04be15b1 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/service-zone-create/loader.ts @@ -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", diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx index 4698810e51..87283058ec 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/components/create-shipping-options-form/create-shipping-options-form.tsx @@ -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.DETAILS) @@ -77,7 +77,7 @@ export function CreateShippingOptionsForm({ fields: "id,currency_code", }) - const form = useForm>({ + const form = useForm>({ 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 = diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts index 234ea0cb14..ed7d531f00 100644 --- a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-create/loader.ts @@ -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: diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx new file mode 100644 index 0000000000..c5d0dbcc68 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/edit-shipping-options-pricing-form.tsx @@ -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 = {} + 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 = {} + 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 + +type EditShippingOptionPricingFormProps = { + shippingOption: ShippingOptionDTO & { prices: PriceDTO[] } +} + +export function EditShippingOptionsPricingForm({ + shippingOption, +}: EditShippingOptionPricingFormProps) { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const form = useForm>({ + 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({}) + + 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 ( + +
+ +
+ + + + +
+
+ + +
+ +
+
+
+
+ ) +} + +const columnHelper = createColumnHelper< + ExtendedProductDTO | ProductVariantDTO +>() + +const useColumns = ({ + currencies = [], + regions = [], +}: { + currencies?: CurrencyDTO[] + regions?: RegionDTO[] +}) => { + const { t } = useTranslation() + + const colDefs: ColumnDef[] = + useMemo(() => { + return [ + ...currencies.map((currency) => { + return columnHelper.display({ + header: t("fields.priceTemplate", { + regionOrCountry: currency.code.toUpperCase(), + }), + cell: ({ row, table }) => { + return ( + } + field={`currency_prices.${currency.code}`} + /> + ) + }, + }) + }), + ...regions.map((region) => { + return columnHelper.display({ + header: t("fields.priceTemplate", { + regionOrCountry: region.name, + }), + cell: ({ row, table }) => { + return ( + c.code === region.currency_code + )} + meta={table.options.meta as DataGridMeta} + field={`region_prices.${region.id}`} + /> + ) + }, + }) + }), + ] + }, [t, currencies, regions]) + + return colDefs +} diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts new file mode 100644 index 0000000000..9839576e71 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/components/create-shipping-options-form/index.ts @@ -0,0 +1 @@ +export * from "./edit-shipping-options-pricing-form.tsx" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts new file mode 100644 index 0000000000..d841b62cdb --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/index.ts @@ -0,0 +1 @@ +export { ShippingOptionsEditPricing as Component } from "./shipping-options-edit-pricing" diff --git a/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx new file mode 100644 index 0000000000..9c4c70fa84 --- /dev/null +++ b/packages/admin-next/dashboard/src/v2-routes/shipping/shipping-options-edit-pricing/shipping-options-edit-pricing.tsx @@ -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 ( + + {shippingOption && ( + + )} + + ) +} diff --git a/packages/cli/medusa-dev-cli/README.md b/packages/cli/medusa-dev-cli/README.md index b30a83954c..4cdbfb38fb 100644 --- a/packages/cli/medusa-dev-cli/README.md +++ b/packages/cli/medusa-dev-cli/README.md @@ -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` diff --git a/packages/core/core-flows/src/common/index.ts b/packages/core/core-flows/src/common/index.ts index e99533ae88..013c43af22 100644 --- a/packages/core/core-flows/src/common/index.ts +++ b/packages/core/core-flows/src/common/index.ts @@ -1,2 +1,3 @@ export * from "./steps/remove-remote-links" export * from "./steps/use-remote-query" +export * from "./steps/create-remote-links" diff --git a/packages/core/core-flows/src/common/steps/create-remote-links.ts b/packages/core/core-flows/src/common/steps/create-remote-links.ts new file mode 100644 index 0000000000..08002cc391 --- /dev/null +++ b/packages/core/core-flows/src/common/steps/create-remote-links.ts @@ -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( + ContainerRegistrationKeys.REMOTE_LINK + ) + await link.create(data) + + return new StepResponse(data, data) + }, + async (createdLinks, { container }) => { + if (!createdLinks) { + return + } + + const link = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + await link.dismiss(createdLinks) + } +) diff --git a/packages/core/core-flows/src/order/steps/create-return.ts b/packages/core/core-flows/src/order/steps/create-return.ts new file mode 100644 index 0000000000..3d021256af --- /dev/null +++ b/packages/core/core-flows/src/order/steps/create-return.ts @@ -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( + ModuleRegistrationName.ORDER + ) + + const created = await service.createReturn(data) + return new StepResponse(created, created) + }, + async (createdId, { container }) => { + if (!createdId) { + return + } + + const service = container.resolve( + ModuleRegistrationName.ORDER + ) + + // TODO: delete return + } +) diff --git a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts b/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts index 50294f0f82..3ecb11bd1e 100644 --- a/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts +++ b/packages/core/core-flows/src/order/steps/get-item-tax-lines.ts @@ -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( @@ -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) } diff --git a/packages/core/core-flows/src/order/workflows/create-return.ts b/packages/core/core-flows/src/order/workflows/create-return.ts new file mode 100644 index 0000000000..4cfc4d1431 --- /dev/null +++ b/packages/core/core-flows/src/order/workflows/create-return.ts @@ -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 + 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 + 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["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 + ): WorkflowData => { + 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) + } +) diff --git a/packages/core/core-flows/src/order/workflows/index.ts b/packages/core/core-flows/src/order/workflows/index.ts index 40bedfb9fb..cd814fee4c 100644 --- a/packages/core/core-flows/src/order/workflows/index.ts +++ b/packages/core/core-flows/src/order/workflows/index.ts @@ -1,2 +1,3 @@ export * from "./create-orders" export * from "./update-tax-lines" +export * from "./create-return" diff --git a/packages/core/modules-sdk/src/remote-link.ts b/packages/core/modules-sdk/src/remote-link.ts index 21b25d7b7f..2eb5209660 100644 --- a/packages/core/modules-sdk/src/remote-link.ts +++ b/packages/core/modules-sdk/src/remote-link.ts @@ -14,7 +14,7 @@ export type DeleteEntityInput = { } export type RestoreEntityInput = DeleteEntityInput -type LinkDefinition = { +export type LinkDefinition = { [moduleName: string]: { [fieldName: string]: string } diff --git a/packages/core/types/src/common/index.ts b/packages/core/types/src/common/index.ts index d3a280a643..2717f4ecc6 100644 --- a/packages/core/types/src/common/index.ts +++ b/packages/core/types/src/common/index.ts @@ -3,3 +3,4 @@ export * from "./rule" export * from "./batch" export * from "./config-module" export * from "./medusa-container" +export * from "./with-calculated" diff --git a/packages/core/types/src/common/with-calculated.ts b/packages/core/types/src/common/with-calculated.ts new file mode 100644 index 0000000000..374fc116a1 --- /dev/null +++ b/packages/core/types/src/common/with-calculated.ts @@ -0,0 +1,3 @@ +export interface WithCalculatedPrice { + calculated_price: { calculated_amount: number } +} diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index a4931eb8fb..06ab9c209c 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -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 } + )[] } /** diff --git a/packages/core/types/src/order/common.ts b/packages/core/types/src/order/common.ts index 9342d3517d..61b4685c38 100644 --- a/packages/core/types/src/order/common.ts +++ b/packages/core/types/src/order/common.ts @@ -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 */ diff --git a/packages/core/types/src/order/mutations.ts b/packages/core/types/src/order/mutations.ts index ba594a8db4..dcbc5327fb 100644 --- a/packages/core/types/src/order/mutations.ts +++ b/packages/core/types/src/order/mutations.ts @@ -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 diff --git a/packages/core/types/src/workflow/index.ts b/packages/core/types/src/workflow/index.ts index 4c44c78b6c..9b592a1093 100644 --- a/packages/core/types/src/workflow/index.ts +++ b/packages/core/types/src/workflow/index.ts @@ -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" diff --git a/packages/core/types/src/workflow/order/create-return-order.ts b/packages/core/types/src/workflow/order/create-return-order.ts new file mode 100644 index 0000000000..8ce1a3b01e --- /dev/null +++ b/packages/core/types/src/workflow/order/create-return-order.ts @@ -0,0 +1,26 @@ +import { BigNumberInput } from "../../totals" + +interface CreateOrderReturnItem { + id: string + quantity: BigNumberInput + internal_note?: string + reason_id?: string + metadata?: Record +} + +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 +} diff --git a/packages/core/types/src/workflow/order/index.ts b/packages/core/types/src/workflow/order/index.ts new file mode 100644 index 0000000000..29b175eae9 --- /dev/null +++ b/packages/core/types/src/workflow/order/index.ts @@ -0,0 +1 @@ +export * from "./create-return-order" diff --git a/packages/core/utils/src/link/links.ts b/packages/core/utils/src/link/links.ts index 745da8aa52..1e4d3cec66 100644 --- a/packages/core/utils/src/link/links.ts +++ b/packages/core/utils/src/link/links.ts @@ -80,4 +80,10 @@ export const LINKS = { Modules.PAYMENT, "payment_collection_id" ), + OrderFulfillment: composeLinkName( + Modules.ORDER, + "order_id", + Modules.FULFILLMENT, + "fulfillment_id" + ), } diff --git a/packages/core/utils/src/modules-sdk/decorators/module.ts b/packages/core/utils/src/modules-sdk/decorators/module.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts b/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts index b8189fb8ea..599713a6fd 100644 --- a/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts +++ b/packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts @@ -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]) } } diff --git a/packages/core/workflows-sdk/src/utils/composer/type.ts b/packages/core/workflows-sdk/src/utils/composer/type.ts index e7f2691042..31fa467ce8 100644 --- a/packages/core/workflows-sdk/src/utils/composer/type.ts +++ b/packages/core/workflows-sdk/src/utils/composer/type.ts @@ -201,7 +201,7 @@ export type ReturnWorkflow< runAsStep: ({ input, }: { - input: TData + input: TData | WorkflowData }) => ReturnType> run: ( ...args: Parameters< diff --git a/packages/medusa-interfaces/.babelrc b/packages/medusa-interfaces/.babelrc deleted file mode 100644 index 41dc577de6..0000000000 --- a/packages/medusa-interfaces/.babelrc +++ /dev/null @@ -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"] - } - } -} \ No newline at end of file diff --git a/packages/medusa-interfaces/.gitignore b/packages/medusa-interfaces/.gitignore deleted file mode 100644 index aacc207d2e..0000000000 --- a/packages/medusa-interfaces/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -dist/ -node_modules/ -.DS_store -.env* -/*.js -!index.js -yarn.lock diff --git a/packages/medusa-interfaces/CHANGELOG.md b/packages/medusa-interfaces/CHANGELOG.md deleted file mode 100644 index 6ecc283bdb..0000000000 --- a/packages/medusa-interfaces/CHANGELOG.md +++ /dev/null @@ -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)) diff --git a/packages/medusa-interfaces/package.json b/packages/medusa-interfaces/package.json deleted file mode 100644 index d5636081cc..0000000000 --- a/packages/medusa-interfaces/package.json +++ /dev/null @@ -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" -} diff --git a/packages/medusa-interfaces/src/__tests__/base-service.js b/packages/medusa-interfaces/src/__tests__/base-service.js deleted file mode 100644 index 76b5055c1f..0000000000 --- a/packages/medusa-interfaces/src/__tests__/base-service.js +++ /dev/null @@ -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, - }) - }) - }) -}) diff --git a/packages/medusa-interfaces/src/base-service.js b/packages/medusa-interfaces/src/base-service.js deleted file mode 100644 index 09f876a3fe..0000000000 --- a/packages/medusa-interfaces/src/base-service.js +++ /dev/null @@ -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)) - } -} diff --git a/packages/medusa-interfaces/src/file-service.js b/packages/medusa-interfaces/src/file-service.js deleted file mode 100644 index d59ded3128..0000000000 --- a/packages/medusa-interfaces/src/file-service.js +++ /dev/null @@ -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 diff --git a/packages/medusa-interfaces/src/fulfillment-service.js b/packages/medusa-interfaces/src/fulfillment-service.js deleted file mode 100644 index 880c7d6b08..0000000000 --- a/packages/medusa-interfaces/src/fulfillment-service.js +++ /dev/null @@ -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 diff --git a/packages/medusa-interfaces/src/index.js b/packages/medusa-interfaces/src/index.js deleted file mode 100644 index 9cf865cf57..0000000000 --- a/packages/medusa-interfaces/src/index.js +++ /dev/null @@ -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" diff --git a/packages/medusa-interfaces/src/notification-service.js b/packages/medusa-interfaces/src/notification-service.js deleted file mode 100644 index 04ed883a11..0000000000 --- a/packages/medusa-interfaces/src/notification-service.js +++ /dev/null @@ -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 diff --git a/packages/medusa-interfaces/src/oauth-service.js b/packages/medusa-interfaces/src/oauth-service.js deleted file mode 100644 index f001d75254..0000000000 --- a/packages/medusa-interfaces/src/oauth-service.js +++ /dev/null @@ -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 diff --git a/packages/medusa-interfaces/src/payment-service.js b/packages/medusa-interfaces/src/payment-service.js deleted file mode 100644 index 7492d4304e..0000000000 --- a/packages/medusa-interfaces/src/payment-service.js +++ /dev/null @@ -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 diff --git a/packages/medusa-interfaces/src/search-service.js b/packages/medusa-interfaces/src/search-service.js deleted file mode 100644 index df818a2d83..0000000000 --- a/packages/medusa-interfaces/src/search-service.js +++ /dev/null @@ -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.} - 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.} - 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 diff --git a/packages/medusa-interfaces/tsconfig.json b/packages/medusa-interfaces/tsconfig.json deleted file mode 100644 index 6052d283a2..0000000000 --- a/packages/medusa-interfaces/tsconfig.json +++ /dev/null @@ -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" - ] -} diff --git a/packages/medusa/index.d.ts b/packages/medusa/index.d.ts deleted file mode 100644 index 80c99ed531..0000000000 --- a/packages/medusa/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "medusa-interfaces" diff --git a/packages/medusa/package.json b/packages/medusa/package.json index d0f7dcd302..e4663ee790 100644 --- a/packages/medusa/package.json +++ b/packages/medusa/package.json @@ -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", diff --git a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts index 27bcdba30d..0aa40c53c9 100644 --- a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts @@ -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 { diff --git a/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts b/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts index cc39659544..579d01afdb 100644 --- a/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts +++ b/packages/modules/link-modules/src/definitions/fulfillment-set-location.ts @@ -49,6 +49,9 @@ export const LocationFulfillmentSet: ModuleJoinerConfig = { }, { serviceName: Modules.FULFILLMENT, + fieldAlias: { + location: "locations_link.location", + }, relationship: { serviceName: LINKS.LocationFulfillmentSet, primaryKey: "fulfillment_set_id", diff --git a/packages/modules/link-modules/src/definitions/index.ts b/packages/modules/link-modules/src/definitions/index.ts index 288b03e548..37498fc482 100644 --- a/packages/modules/link-modules/src/definitions/index.ts +++ b/packages/modules/link-modules/src/definitions/index.ts @@ -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" diff --git a/packages/modules/link-modules/src/definitions/order-fulfillment.ts b/packages/modules/link-modules/src/definitions/order-fulfillment.ts new file mode 100644 index 0000000000..f134c3370c --- /dev/null +++ b/packages/modules/link-modules/src/definitions/order-fulfillment.ts @@ -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", + }, + }, + ], +} diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 41fa14ad26..70f2fe89ab 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -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 diff --git a/yarn.lock b/yarn.lock index 7cc06b870d..6fd0dca3d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"