diff --git a/.changeset/soft-boats-wash.md b/.changeset/soft-boats-wash.md new file mode 100644 index 0000000000..9358d3e93c --- /dev/null +++ b/.changeset/soft-boats-wash.md @@ -0,0 +1,9 @@ +--- +"@medusajs/fulfillment": patch +"@medusajs/core-flows": patch +"@medusajs/types": patch +"@medusajs/utils": patch +"@medusajs/medusa": patch +--- + +chore(types, api): support shipping option type api endpoints diff --git a/integration-tests/http/__tests__/shipping-option-type/admin/shipping-option-type.spec.ts b/integration-tests/http/__tests__/shipping-option-type/admin/shipping-option-type.spec.ts new file mode 100644 index 0000000000..60ab211284 --- /dev/null +++ b/integration-tests/http/__tests__/shipping-option-type/admin/shipping-option-type.spec.ts @@ -0,0 +1,105 @@ +import { medusaIntegrationTestRunner } from "@medusajs/test-utils" +import { adminHeaders, createAdminUser, } from "../../../../helpers/create-admin-user" + +jest.setTimeout(30000) + +medusaIntegrationTestRunner({ + env: {}, + testSuite: ({ dbConnection, getContainer, api }) => { + let type1 + let type2 + + beforeEach(async () => { + const container = getContainer() + await createAdminUser(dbConnection, adminHeaders, container) + + type1 = ( + await api.post( + "/admin/shipping-option-types", + { + label: "Test1", + code: "test1", + description: "Test1 description", + }, + adminHeaders + ) + ).data.shipping_option_type + + type2 = ( + await api.post( + "/admin/shipping-option-types", + { + label: "Test2", + code: "test2", + description: "Test2 description", + }, + adminHeaders + ) + ).data.shipping_option_type + }) + + describe("/admin/shipping-option-types", () => { + it("returns a list of shipping option types", async () => { + const res = await api.get("/admin/shipping-option-types", adminHeaders) + + expect(res.status).toEqual(200) + expect(res.data.shipping_option_types).toEqual( + expect.arrayContaining([ + { + id: expect.stringMatching(/sotype_.{24}/), + label: "Test1", + code: "test1", + description: "Test1 description", + created_at: expect.any(String), + updated_at: expect.any(String), + }, + { + id: expect.stringMatching(/sotype_.{24}/), + label: "Test2", + code: "test2", + description: "Test2 description", + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ]) + ) + }) + + it("returns a list of shipping option types matching free text search param", async () => { + const res = await api.get("/admin/shipping-option-types?code=test1", adminHeaders) + + expect(res.status).toEqual(200) + + expect(res.data.shipping_option_types).toEqual([ + { + id: expect.stringMatching(/sotype_.{24}/), + label: "Test1", + code: "test1", + description: "Test1 description", + created_at: expect.any(String), + updated_at: expect.any(String), + }, + ]) + }) + }) + + describe("/admin/shipping-option-types/:id", () => { + it("returns a shipping option type", async () => { + const res = await api.get( + `/admin/shipping-option-types/${type1.id}`, + adminHeaders + ) + + expect(res.status).toEqual(200) + expect(res.data.shipping_option_type).toEqual({ + id: expect.stringMatching(/sotype_.{24}/), + label: "Test1", + code: "test1", + description: "Test1 description", + created_at: expect.any(String), + updated_at: expect.any(String), + }) + }) + }) + }, +}) diff --git a/packages/core/core-flows/src/shipping-options/index.ts b/packages/core/core-flows/src/shipping-options/index.ts index c1f49c23fa..68de82c9f9 100644 --- a/packages/core/core-flows/src/shipping-options/index.ts +++ b/packages/core/core-flows/src/shipping-options/index.ts @@ -1 +1,2 @@ export * from "./steps" +export * from "./workflows" diff --git a/packages/core/core-flows/src/shipping-options/steps/create-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/steps/create-shipping-option-types.ts new file mode 100644 index 0000000000..07e9f94ada --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/steps/create-shipping-option-types.ts @@ -0,0 +1,29 @@ +import { FulfillmentTypes, IFulfillmentModuleService, } from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" + +export const createShippingOptionTypesStepId = "create-shipping-option-types" +/** + * This step creates one or more shipping option types. + */ +export const createShippingOptionTypesStep = createStep( + createShippingOptionTypesStepId, + async (data: FulfillmentTypes.CreateShippingOptionTypeDTO[], { container }) => { + const service = container.resolve(Modules.FULFILLMENT) + + const created = await service.createShippingOptionTypes(data) + return new StepResponse( + created, + created.map((shippingOptionType) => shippingOptionType.id) + ) + }, + async (createdIds, { container }) => { + if (!createdIds?.length) { + return + } + + const service = container.resolve(Modules.FULFILLMENT) + + await service.deleteShippingOptionTypes(createdIds) + } +) diff --git a/packages/core/core-flows/src/shipping-options/steps/delete-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/steps/delete-shipping-option-types.ts new file mode 100644 index 0000000000..95c9c53af1 --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/steps/delete-shipping-option-types.ts @@ -0,0 +1,31 @@ +import { IFulfillmentModuleService, } from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" + +/** + * The IDs of the shipping option types to delete. + */ +export type DeleteShippingOptionTypesStepInput = string[] + +export const deleteShippingOptionTypesStepId = "delete-shipping-option-types" +/** + * This step deletes one or more shipping option types. + */ +export const deleteShippingOptionTypesStep = createStep( + deleteShippingOptionTypesStepId, + async (ids: DeleteShippingOptionTypesStepInput, { container }) => { + const service = container.resolve(Modules.FULFILLMENT) + + await service.softDeleteShippingOptionTypes(ids) + return new StepResponse(void 0, ids) + }, + async (prevIds, { container }) => { + if (!prevIds?.length) { + return + } + + const service = container.resolve(Modules.FULFILLMENT) + + await service.restoreShippingOptionTypes(prevIds) + } +) diff --git a/packages/core/core-flows/src/shipping-options/steps/index.ts b/packages/core/core-flows/src/shipping-options/steps/index.ts index 3e8969a130..a5721754e9 100644 --- a/packages/core/core-flows/src/shipping-options/steps/index.ts +++ b/packages/core/core-flows/src/shipping-options/steps/index.ts @@ -1 +1,4 @@ export * from "./list-shipping-options-for-context" +export * from "./create-shipping-option-types" +export * from "./delete-shipping-option-types" +export * from "./update-shipping-option-types" diff --git a/packages/core/core-flows/src/shipping-options/steps/update-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/steps/update-shipping-option-types.ts new file mode 100644 index 0000000000..549476df64 --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/steps/update-shipping-option-types.ts @@ -0,0 +1,52 @@ +import { FulfillmentTypes, IFulfillmentModuleService, } from "@medusajs/framework/types" +import { getSelectsAndRelationsFromObjectArray, Modules, } from "@medusajs/framework/utils" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" + +/** + * The data to identify and update the shipping option types. + */ +export type UpdateShippingOptionTypesStepInput = { + /** + * The filters to select the shipping option types to update. + */ + selector: FulfillmentTypes.FilterableShippingOptionTypeProps + /** + * The data to update the shipping option types with. + */ + update: FulfillmentTypes.UpdateShippingOptionTypeDTO +} + +export const updateShippingOptionTypesStepId = "update-shipping-option-types" +/** + * This step updates shipping option types matching the specified filters. + */ +export const updateShippingOptionTypesStep = createStep( + updateShippingOptionTypesStepId, + async (data: UpdateShippingOptionTypesStepInput, { container }) => { + const service = container.resolve(Modules.FULFILLMENT) + + const { selects, relations } = getSelectsAndRelationsFromObjectArray([ + data.update, + ]) + + const prevData = await service.listShippingOptionTypes(data.selector, { + select: selects, + relations, + }) + + const shippingOptionTypes = await service.updateShippingOptionTypes( + data.selector, + data.update + ) + return new StepResponse(shippingOptionTypes, prevData) + }, + async (prevData, { container }) => { + if (!prevData?.length) { + return + } + + const service = container.resolve(Modules.FULFILLMENT) + + await service.upsertShippingOptionTypes(prevData) + } +) diff --git a/packages/core/core-flows/src/shipping-options/workflows/create-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/workflows/create-shipping-option-types.ts new file mode 100644 index 0000000000..f8e04f605e --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/workflows/create-shipping-option-types.ts @@ -0,0 +1,80 @@ +import { AdditionalData, FulfillmentTypes, } from "@medusajs/framework/types" +import { ShippingOptionTypeWorkflowEvents } from "@medusajs/framework/utils" +import { + createHook, + createWorkflow, + transform, + WorkflowData, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { emitEventStep } from "../../common" +import { createShippingOptionTypesStep } from "../steps" + +/** + * The data to create one or more shipping option types, along with custom data that's passed to the workflow's hooks. + */ +export type CreateShippingOptionTypesWorkflowInput = { + /** + * The shipping option types to create. + */ + shipping_option_types: FulfillmentTypes.CreateShippingOptionTypeDTO[] +} & AdditionalData + +export const createShippingOptionTypesWorkflowId = "create-shipping-option-types" +/** + * This workflow creates one or more shipping option types. It's used by the + * [Create Shipping Option Type Admin API Route](TODO HERE). + * + * This workflow has a hook that allows you to perform custom actions on the created shipping option types. For example, you can pass under `additional_data` custom data that + * allows you to create custom data models linked to the shipping option types. + * + * You can also use this workflow within your own custom workflows, allowing you to wrap custom logic around shipping option type creation. + * + * @example + * const { result } = await createShippingOptionTypesWorkflow(container) + * .run({ + * input: { + * shipping_option_types: [ + * { + * label: "Standard ", + * code: "standard", + * description: "Ship in 2-3 days." + * } + * ], + * additional_data: { + * erp_id: "123" + * } + * } + * }) + * + * @summary + * + * Create one or more shipping option types. + * + * @property hooks.shippingOptionTypesCreated - This hook is executed after the shipping option types are created. You can consume this hook to perform custom actions on the created shipping option types. + */ +export const createShippingOptionTypesWorkflow = createWorkflow( + createShippingOptionTypesWorkflowId, + (input: WorkflowData) => { + const shippingOptionTypes = createShippingOptionTypesStep(input.shipping_option_types) + const shippingOptionTypesCreated = createHook("shippingOptionTypesCreated", { + shipping_option_types: shippingOptionTypes, + additional_data: input.additional_data, + }) + + const typeIdEvents = transform({ shippingOptionTypes }, ({ shippingOptionTypes }) => { + return shippingOptionTypes.map((v) => { + return { id: v.id } + }) + }) + + emitEventStep({ + eventName: ShippingOptionTypeWorkflowEvents.CREATED, + data: typeIdEvents, + }) + + return new WorkflowResponse(shippingOptionTypes, { + hooks: [shippingOptionTypesCreated], + }) + } +) diff --git a/packages/core/core-flows/src/shipping-options/workflows/delete-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/workflows/delete-shipping-option-types.ts new file mode 100644 index 0000000000..57f7114ff4 --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/workflows/delete-shipping-option-types.ts @@ -0,0 +1,76 @@ +import { Modules, ShippingOptionTypeWorkflowEvents } from "@medusajs/framework/utils" +import { + createHook, + createWorkflow, + parallelize, + transform, + WorkflowData, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { emitEventStep } from "../../common/steps/emit-event" +import { removeRemoteLinkStep } from "../../common/steps/remove-remote-links" +import { deleteShippingOptionTypesStep } from "../steps" + +/** + * The data to delete one or more shipping option types. + */ +export type DeleteShippingOptionTypesWorkflowInput = { + /** + * The IDs of the types to delete. + */ + ids: string[] +} + +export const deleteShippingOptionTypesWorkflowId = "delete-shipping-option-types" +/** + * This workflow deletes one or more shipping-option types. It's used by the + * [Delete Shipping Option Types Admin API Route](TODO HERE). + * + * This workflow has a hook that allows you to perform custom actions after the shipping-option types are deleted. For example, + * you can delete custom records linked to the shipping-option types. + * + * You can also use this workflow within your own custom workflows, allowing you to wrap custom logic around shipping option type deletion. + * + * @example + * const { result } = await deleteShippingOptionTypesWorkflow(container) + * .run({ + * input: { + * ids: ["sotype_123"], + * } + * }) + * + * @summary + * + * Delete one or more shippingOption types. + * + * @property hooks.shippingOptionTypesDeleted - This hook is executed after the types are deleted. You can consume this hook to perform custom actions on the deleted types. + */ +export const deleteShippingOptionTypesWorkflow = createWorkflow( + deleteShippingOptionTypesWorkflowId, + (input: WorkflowData) => { + const deletedShippingOptionTypes = deleteShippingOptionTypesStep(input.ids) + const shippingOptionTypesDeleted = createHook("shippingOptionTypesDeleted", { + ids: input.ids, + }) + + const typeIdEvents = transform({ input }, ({ input }) => { + return input.ids?.map((id) => { + return { id } + }) + }) + + parallelize( + removeRemoteLinkStep({ + [Modules.FULFILLMENT]: { shipping_option_type_id: input.ids }, + }), + emitEventStep({ + eventName: ShippingOptionTypeWorkflowEvents.DELETED, + data: typeIdEvents, + }) + ) + + return new WorkflowResponse(deletedShippingOptionTypes, { + hooks: [shippingOptionTypesDeleted], + }) + } +) diff --git a/packages/core/core-flows/src/shipping-options/workflows/index.ts b/packages/core/core-flows/src/shipping-options/workflows/index.ts new file mode 100644 index 0000000000..e79de66fb3 --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/workflows/index.ts @@ -0,0 +1,3 @@ +export * from "./create-shipping-option-types" +export * from "./delete-shipping-option-types" +export * from "./update-shipping-option-types" diff --git a/packages/core/core-flows/src/shipping-options/workflows/update-shipping-option-types.ts b/packages/core/core-flows/src/shipping-options/workflows/update-shipping-option-types.ts new file mode 100644 index 0000000000..99fae15e7a --- /dev/null +++ b/packages/core/core-flows/src/shipping-options/workflows/update-shipping-option-types.ts @@ -0,0 +1,90 @@ +import { AdditionalData, FulfillmentTypes } from "@medusajs/framework/types" +import { ShippingOptionTypeWorkflowEvents } from "@medusajs/framework/utils" +import { + createHook, + createWorkflow, + transform, + WorkflowData, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { emitEventStep } from "../../common/steps/emit-event" +import { updateShippingOptionTypesStep } from "../steps" + +/** + * The data to update one or more shipping option types, along with custom data that's passed to the workflow's hooks. + */ +type UpdateShippingOptionTypesWorkflowInput = { + /** + * The filters to select the shipping option types to update. + */ + selector: FulfillmentTypes.FilterableShippingOptionTypeProps + /** + * The data to update in the shipping option types. + */ + update: FulfillmentTypes.UpdateShippingOptionTypeDTO +} & AdditionalData + +export const updateShippingOptionTypesWorkflowId = "update-shipping-option-types" +/** + * This workflow updates one or more shipping option types. It's used by the + * [Update Shipping Option Type Admin API Route](TODO HERE). + * + * This workflow has a hook that allows you to perform custom actions on the updated shipping option types. For example, you can pass under `additional_data` custom data that + * allows you to update custom data models linked to the shipping option types. + * + * You can also use this workflow within your own custom workflows, allowing you to wrap custom logic around shipping option type updates. + * + * @example + * const { result } = await updateShippingOptionTypesWorkflow(container) + * .run({ + * input: { + * selector: { + * id: "ptyp_123" + * }, + * update: { + * value: "clothing" + * }, + * additional_data: { + * erp_id: "123" + * } + * } + * }) + * + * @summary + * + * Update one or more shipping option types. + * + * @property hooks.shippingOptionTypesUpdated - This hook is executed after the shipping option types are updated. You can consume this hook to perform custom actions on the updated shipping option types. + */ +export const updateShippingOptionTypesWorkflow = createWorkflow( + updateShippingOptionTypesWorkflowId, + (input: WorkflowData) => { + const updatedShippingOptionTypes = updateShippingOptionTypesStep(input) + const shippingOptionTypesUpdated = createHook("shippingOptionTypesUpdated", { + shipping_option_types: updatedShippingOptionTypes, + additional_data: input.additional_data, + }) + + const typeIdEvents = transform( + { updatedShippingOptionTypes }, + ({ updatedShippingOptionTypes }) => { + const arr = Array.isArray(updatedShippingOptionTypes) + ? updatedShippingOptionTypes + : [updatedShippingOptionTypes] + + return arr?.map((v) => { + return { id: v.id } + }) + } + ) + + emitEventStep({ + eventName: ShippingOptionTypeWorkflowEvents.UPDATED, + data: typeIdEvents, + }) + + return new WorkflowResponse(updatedShippingOptionTypes, { + hooks: [shippingOptionTypesUpdated], + }) + } +) diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option-type.ts b/packages/core/types/src/fulfillment/mutations/shipping-option-type.ts index 785ec365bb..ba6edb0925 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option-type.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option-type.ts @@ -10,17 +10,12 @@ export interface CreateShippingOptionTypeDTO { /** * The description of the shipping option type. */ - description: string + description?: string /** * The code of the shipping option type. */ code: string - - /** - * The associated shipping option's ID. - */ - shipping_option_id: string } /** @@ -31,5 +26,10 @@ export interface UpdateShippingOptionTypeDTO /** * The ID of the shipping option type. */ - id: string + id?: string } + +/** + * A shipping option type to be created or updated. + */ +export interface UpsertShippingOptionTypeDTO extends UpdateShippingOptionTypeDTO {} diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index a67ab827b5..78ce919329 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -35,7 +35,7 @@ export interface CreateShippingOptionDTO { /** * The shipping option type associated with the shipping option. */ - type: Omit + type: CreateShippingOptionTypeDTO /** * The data necessary for the associated fulfillment provider to process the shipping option diff --git a/packages/core/types/src/fulfillment/service.ts b/packages/core/types/src/fulfillment/service.ts index 127b470dca..21eaeefe22 100644 --- a/packages/core/types/src/fulfillment/service.ts +++ b/packages/core/types/src/fulfillment/service.ts @@ -30,25 +30,22 @@ import { CreateServiceZoneDTO, CreateShippingOptionDTO, CreateShippingOptionRuleDTO, + CreateShippingOptionTypeDTO, UpdateFulfillmentDTO, UpdateFulfillmentSetDTO, UpdateGeoZoneDTO, UpdateServiceZoneDTO, UpdateShippingOptionDTO, UpdateShippingOptionRuleDTO, + UpdateShippingOptionTypeDTO, UpdateShippingProfileDTO, UpsertServiceZoneDTO, UpsertShippingOptionDTO, + UpsertShippingOptionTypeDTO, } from "./mutations" import { CreateFulfillmentDTO } from "./mutations/fulfillment" -import { - CreateShippingProfileDTO, - UpsertShippingProfileDTO, -} from "./mutations/shipping-profile" -import { - CalculatedShippingOptionPrice, - ValidateFulfillmentDataContext, -} from "./provider" +import { CreateShippingProfileDTO, UpsertShippingProfileDTO, } from "./mutations/shipping-profile" +import { CalculatedShippingOptionPrice, ValidateFulfillmentDataContext, } from "./provider" /** * The main service interface for the Fulfillment Module. @@ -72,12 +69,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -114,12 +111,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the fulfillment set: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -174,14 +171,14 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the fulfillment set: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const [fulfillmentSets, count] = * await fulfillmentModuleService.listAndCountFulfillmentSets( @@ -394,14 +391,14 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const serviceZone = * await fulfillmentModuleService.retrieveServiceZone( @@ -438,12 +435,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the service zone: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -500,12 +497,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the service zone: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -766,12 +763,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -808,12 +805,12 @@ export interface IFulfillmentModuleService extends IModuleService { * To specify relations that should be retrieved within the geo zone: * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const geoZones = await fulfillmentModuleService.listGeoZones( * { @@ -868,12 +865,12 @@ export interface IFulfillmentModuleService extends IModuleService { * To specify relations that should be retrieved within the geo zone: * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const [geoZones, count] = * await fulfillmentModuleService.listAndCountGeoZones( @@ -1094,14 +1091,14 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const shippingOption = * await fulfillmentModuleService.retrieveShippingOption( @@ -1138,14 +1135,14 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const shippingOptions = * await fulfillmentModuleService.listShippingOptions( @@ -1206,12 +1203,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -1275,12 +1272,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -1575,12 +1572,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -1619,14 +1616,14 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping profile: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const shippingProfiles = * await fulfillmentModuleService.listShippingProfiles( @@ -1683,12 +1680,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping profile: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -1954,12 +1951,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2000,12 +1997,12 @@ export interface IFulfillmentModuleService extends IModuleService { * To specify relations that should be retrieved within the shipping option rule: * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: - * + * * ```ts * const shippingOptionRules = * await fulfillmentModuleService.listShippingOptionRules( @@ -2062,12 +2059,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option rule: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2251,12 +2248,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2275,6 +2272,146 @@ export interface IFulfillmentModuleService extends IModuleService { sharedContext?: Context ): Promise + /** + * This method creates shipping option types. + * + * @param {CreateShippingOptionTypeDTO[]} data - The shipping option types to be created. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The created shipping option type. + * + * @example + * const shippingOptionTypes = + * await fulfillmentModuleService.createShippingOptionTypes([ + * { + * label: "Standard", + * code: "standard", + * description: "Ship in 2-3 days." + * }, + * { + * label: "Express", + * code: "express", + * description: "Ship in 24 hours." + * } + * ]) + */ + createShippingOptionTypes( + data: CreateShippingOptionTypeDTO[], + sharedContext?: Context + ): Promise + + /** + * This method creates a shipping option type. + * + * @param {CreateShippingOptionTypeDTO} data - The shipping option type to be created. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The created shipping option type. + * + * @example + * const shippingOption = + * await fulfillmentModuleService.createShippingOptionTypes({ + * label: "Standard", + * code: "standard", + * description: "Ship in 2-3 days." + * }) + */ + createShippingOptionTypes( + data: CreateShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + /** + * This method updates an existing shipping option type. + * + * @param {string} id - The ID of the shipping option type. + * @param {UpdateShippingOptionTypeDTO} data - The attributes to update in the shipping option type. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The updated shipping option type. + * + * @example + * const shippingOptionType = + * await fulfillmentModuleService.updateShippingOptionTypes( + * "sotype_123", + * { + * label: "Express", + * } + * ) + */ + updateShippingOptionTypes( + id: string, + data: UpdateShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + /** + * This method updates existing shipping option types matching the specified filters. + * + * @param {FilterableShippingOptionProps} selector - The filters specifying which shipping option types to update. + * @param {FilterableShippingOptionTypeProps} data - The attributes to update in the shipping option type. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The updated shipping option types. + * + * @example + * const shippingOptions = + * await fulfillmentModuleService.updateShippingOptionTypes( + * { + * id: ["sotype_123", "sotype_321"], + * }, + * { + * label: "Express", + * } + * ) + */ + updateShippingOptionTypes( + selector: FilterableShippingOptionTypeProps, + data: UpdateShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + /** + * This method updates or creates a shipping option type if it doesn't exist. + * + * @param {UpsertShippingOptionTypeDTO} data - The attributes in the shipping option type to be created or updated. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The created or updated shipping option type. + * + * @example + * const shippingOptions = + * await fulfillmentModuleService.upsertShippingOptionTypes({ + * id: "sotype_123", + * label: "Express", + * }) + */ + upsertShippingOptionTypes( + data: UpsertShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + /** + * This method updates or creates shipping option types if they don't exist. + * + * @param {UpsertShippingOptionTypeDTO[]} data - The attributes in the shipping option types to be created or updated. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The created or updated shipping option types. + * + * @example + * const shippingOptions = + * await fulfillmentModuleService.upsertShippingOptionTypes([ + * { + * id: "sotype_123", + * label: "Express", + * }, + * { + * label: "Express", + * code: "express", + * description: "Ship in 24 hours." + * } + * ]) + */ + upsertShippingOptionTypes( + data: UpsertShippingOptionTypeDTO[], + sharedContext?: Context + ): Promise + /** * This method retrieves a paginated list of shipping option types based on optional filters and configuration. * @@ -2295,12 +2432,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option type: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2359,12 +2496,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the shipping option type: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2433,6 +2570,49 @@ export interface IFulfillmentModuleService extends IModuleService { */ deleteShippingOptionTypes(id: string, sharedContext?: Context): Promise + /** + * This method soft deletes shipping option types by their IDs. + * + * @param {string[]} ids - The IDs of the shipping option types. + * @param {SoftDeleteReturn} config - An object that is used to specify an entity's related entities that should be soft-deleted when the main entity is soft-deleted. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise>} An object that includes the IDs of related records that were also soft deleted. + * If there are no related records, the promise resolves to `void`. + * + * @example + * await fulfillmentModuleService.softDeleteShippingOptionTypes([ + * "sotype_123", + * "sotype_321", + * ]) + */ + softDeleteShippingOptionTypes( + ids: string[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + + /** + * This method restores soft deleted shipping option types by their IDs. + * + * @param {string[]} ids - The IDs of the shipping option types. + * @param {RestoreReturn} config - Configurations determining which relations to restore along with each of the shipping option types. You can pass to its `returnLinkableKeys` + * property any of the shipping option type's relation attribute names. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise>} An object that includes the IDs of related records that were restored. + * If there are no related records restored, the promise resolves to `void`. + * + * @example + * await fulfillmentModuleService.restoreShippingOptions([ + * "sotype_123", + * "sotype_321", + * ]) + */ + restoreShippingOptionTypes( + ids: string[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + /** * This method retrieves a fulfillment by its ID. * @@ -2453,12 +2633,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2497,12 +2677,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the fulfillment: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts @@ -2566,12 +2746,12 @@ export interface IFulfillmentModuleService extends IModuleService { * ``` * * To specify relations that should be retrieved within the fulfillment: - * + * * :::note - * + * * You can only retrieve data models defined in the same module. To retrieve linked data models * from other modules, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query) instead. - * + * * ::: * * ```ts diff --git a/packages/core/types/src/http/shipping-option/admin/payloads.ts b/packages/core/types/src/http/shipping-option/admin/payloads.ts index 028a214d18..7d5fb495b3 100644 --- a/packages/core/types/src/http/shipping-option/admin/payloads.ts +++ b/packages/core/types/src/http/shipping-option/admin/payloads.ts @@ -8,14 +8,14 @@ export interface AdminCreateShippingOptionRule { operator: RuleOperatorType /** * The attribute of the shipping option rule. - * + * * @example * `enabled_in_store` */ attribute: string /** * The value of the shipping option rule. - * + * * @example * `true` */ @@ -30,13 +30,28 @@ export interface AdminCreateShippingOptionType { /** * The description of the shipping option type. */ - description: string + description?: string /** * The code of the shipping option type. */ code: string } +export interface AdminUpdateShippingOptionType { + /** + * The label of the shipping option type. + */ + label?: string + /** + * The description of the shipping option type. + */ + description?: string + /** + * The code of the shipping option type. + */ + code?: string +} + interface AdminShippingOptionPriceRulePayload { /** * The operator of the shipping option price rule. diff --git a/packages/core/types/src/http/shipping-option/admin/queries.ts b/packages/core/types/src/http/shipping-option/admin/queries.ts index eb4d4a39fc..10bc73294c 100644 --- a/packages/core/types/src/http/shipping-option/admin/queries.ts +++ b/packages/core/types/src/http/shipping-option/admin/queries.ts @@ -1,4 +1,4 @@ -import { OperatorMap } from "../../../dal" +import { BaseFilterable, OperatorMap } from "../../../dal" import { FindParams } from "../../common" export interface AdminShippingOptionListParams extends FindParams { @@ -51,3 +51,36 @@ export interface AdminShippingOptionListParams extends FindParams { */ deleted_at?: OperatorMap } + +export interface AdminShippingOptionTypeListParams + extends FindParams, + BaseFilterable { + /** + * Query or keywords to apply filters on the type's searchable fields. + */ + q?: string + /** + * Filter by shipping option type ID(s). + */ + id?: string | string[] + /** + * Filter by label(s). + */ + label?: string | string[] + /** + * Filter by code(s). + */ + code?: string | string[] + /** + * Apply filters on the creation date. + */ + created_at?: OperatorMap + /** + * Apply filters on the update date. + */ + updated_at?: OperatorMap + /** + * Apply filters on the deletion date. + */ + deleted_at?: OperatorMap +} diff --git a/packages/core/types/src/http/shipping-option/admin/responses.ts b/packages/core/types/src/http/shipping-option/admin/responses.ts index 081b6ae8e4..adc207fe06 100644 --- a/packages/core/types/src/http/shipping-option/admin/responses.ts +++ b/packages/core/types/src/http/shipping-option/admin/responses.ts @@ -1,5 +1,9 @@ import { BatchResponse, DeleteResponse, PaginatedResponse } from "../../common" -import { AdminShippingOption, AdminShippingOptionRule } from "./entities" +import { + AdminShippingOption, + AdminShippingOptionRule, + AdminShippingOptionType, +} from "./entities" export interface AdminShippingOptionResponse { /** @@ -20,3 +24,21 @@ export interface AdminShippingOptionDeleteResponse export type AdminUpdateShippingOptionRulesResponse = BatchResponse + +export interface AdminShippingOptionTypeResponse { + /** + * The shipping option type's details. + */ + shipping_option_type: AdminShippingOptionType +} + +export interface AdminShippingOptionTypeListResponse + extends PaginatedResponse<{ + /** + * The list of shipping option types. + */ + shipping_option_types: AdminShippingOptionType[] + }> {} + +export interface AdminShippingOptionTypeDeleteResponse + extends DeleteResponse<"shipping_option_type"> {} diff --git a/packages/core/types/src/workflow/fulfillment/create-shipping-options.ts b/packages/core/types/src/workflow/fulfillment/create-shipping-options.ts index 1bc4e6cabb..71876e4c51 100644 --- a/packages/core/types/src/workflow/fulfillment/create-shipping-options.ts +++ b/packages/core/types/src/workflow/fulfillment/create-shipping-options.ts @@ -47,7 +47,7 @@ type CreateFlatShippingOptionInputBase = { /** * The description of the shipping option type. */ - description: string + description?: string /** * The code of the shipping option type. */ diff --git a/packages/core/types/src/workflow/fulfillment/update-shipping-options.ts b/packages/core/types/src/workflow/fulfillment/update-shipping-options.ts index 755f95b0b9..f56fdd1f01 100644 --- a/packages/core/types/src/workflow/fulfillment/update-shipping-options.ts +++ b/packages/core/types/src/workflow/fulfillment/update-shipping-options.ts @@ -41,7 +41,7 @@ type UpdateFlatShippingOptionInputBase = { /** * The description of the shipping option type. */ - description: string + description?: string /** * The code of the shipping option type. */ diff --git a/packages/core/utils/src/core-flows/events.ts b/packages/core/utils/src/core-flows/events.ts index c0c5f56604..76f8ee3825 100644 --- a/packages/core/utils/src/core-flows/events.ts +++ b/packages/core/utils/src/core-flows/events.ts @@ -815,6 +815,46 @@ export const FulfillmentWorkflowEvents = { DELIVERY_CREATED: "delivery.created", } +/** + * @category Shipping Option Type + * @customNamespace Fulfillment + */ +export const ShippingOptionTypeWorkflowEvents = { + /** + * Emitted when shipping option types are updated. + * + * @eventPayload + * ```ts + * [{ + * id, // The ID of the shipping option type + * }] + * ``` + */ + UPDATED: "shipping-option-type.updated", + /** + * Emitted when shipping option types are created. + * + * @eventPayload + * ```ts + * [{ + * id, // The ID of the shipping option type + * }] + * ``` + */ + CREATED: "shipping-option-type.created", + /** + * Emitted when shipping option types are deleted. + * + * @eventPayload + * ```ts + * [{ + * id, // The ID of the shipping option type + * }] + * ``` + */ + DELETED: "shipping-option-type.deleted", +} + /** * @category Payment * @customNamespace Payment @@ -842,4 +882,4 @@ export const PaymentEvents = { * ``` */ REFUNDED: "payment.refunded", -} \ No newline at end of file +} diff --git a/packages/medusa/src/api/admin/shipping-option-types/[id]/route.ts b/packages/medusa/src/api/admin/shipping-option-types/[id]/route.ts new file mode 100644 index 0000000000..5bb68e0dce --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/[id]/route.ts @@ -0,0 +1,79 @@ +import { + deleteShippingOptionTypesWorkflow, + updateShippingOptionTypesWorkflow, +} from "@medusajs/core-flows" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +import { refetchShippingOptionType } from "../helpers" +import { + AdminGetShippingOptionTypeParamsType, + AdminUpdateShippingOptionTypeType, +} from "../validators" +import { HttpTypes } from "@medusajs/framework/types" +import { MedusaError } from "@medusajs/framework/utils" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const shippingOptionType = await refetchShippingOptionType( + req.params.id, + req.scope, + req.queryConfig.fields + ) + + res.status(200).json({ shipping_option_type: shippingOptionType }) +} + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const existingShippingOptionType = await refetchShippingOptionType( + req.params.id, + req.scope, + ["id"] + ) + + if (!existingShippingOptionType) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Shipping option type with id "${req.params.id}" not found` + ) + } + + const { result } = await updateShippingOptionTypesWorkflow(req.scope).run({ + input: { + selector: { id: req.params.id }, + update: req.validatedBody, + }, + }) + + const shippingOptionType = await refetchShippingOptionType( + result[0].id, + req.scope, + req.queryConfig.fields + ) + + res.status(200).json({ shipping_option_type: shippingOptionType }) +} + +export const DELETE = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const id = req.params.id + + await deleteShippingOptionTypesWorkflow(req.scope).run({ + input: { ids: [id] }, + }) + + res.status(200).json({ + id, + object: "shipping_option_type", + deleted: true, + }) +} diff --git a/packages/medusa/src/api/admin/shipping-option-types/helpers.ts b/packages/medusa/src/api/admin/shipping-option-types/helpers.ts new file mode 100644 index 0000000000..d645fe6319 --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/helpers.ts @@ -0,0 +1,16 @@ +import { MedusaContainer } from "@medusajs/framework/types" +import { ContainerRegistrationKeys } from "@medusajs/framework/utils" + +export const refetchShippingOptionType = async ( + shippingOptionTypeId: string, + scope: MedusaContainer, + fields: string[] +) => { + const query = scope.resolve(ContainerRegistrationKeys.QUERY) + const { data: shippingOptionTypes } = await query.graph({ + entity: "shipping_option_type", + fields: fields, + filters: { id: shippingOptionTypeId }, + }) + return shippingOptionTypes[0] +} diff --git a/packages/medusa/src/api/admin/shipping-option-types/middlewares.ts b/packages/medusa/src/api/admin/shipping-option-types/middlewares.ts new file mode 100644 index 0000000000..6aa4e12fc4 --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/middlewares.ts @@ -0,0 +1,62 @@ +import * as QueryConfig from "./query-config" +import { MiddlewareRoute } from "@medusajs/framework/http" +import { + validateAndTransformBody, + validateAndTransformQuery, +} from "@medusajs/framework" +import { + AdminCreateShippingOptionType, + AdminGetShippingOptionTypeParams, + AdminGetShippingOptionTypesParams, + AdminUpdateShippingOptionType, +} from "./validators" + +export const adminShippingOptionTypeRoutesMiddlewares: MiddlewareRoute[] = [ + { + method: ["GET"], + matcher: "/admin/shipping-option-types", + middlewares: [ + validateAndTransformQuery( + AdminGetShippingOptionTypesParams, + QueryConfig.listShippingOptionTypesTransformQueryConfig + ), + ], + }, + { + method: ["GET"], + matcher: "/admin/shipping-option-types/:id", + middlewares: [ + validateAndTransformQuery( + AdminGetShippingOptionTypeParams, + QueryConfig.retrieveShippingOptionTypeTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/shipping-option-types", + middlewares: [ + validateAndTransformBody(AdminCreateShippingOptionType), + validateAndTransformQuery( + AdminGetShippingOptionTypeParams, + QueryConfig.retrieveShippingOptionTypeTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/shipping-option-types/:id", + middlewares: [ + validateAndTransformBody(AdminUpdateShippingOptionType), + validateAndTransformQuery( + AdminGetShippingOptionTypeParams, + QueryConfig.retrieveShippingOptionTypeTransformQueryConfig + ), + ], + }, + { + method: ["DELETE"], + matcher: "/admin/shipping-option-types/:id", + middlewares: [], + }, +] diff --git a/packages/medusa/src/api/admin/shipping-option-types/query-config.ts b/packages/medusa/src/api/admin/shipping-option-types/query-config.ts new file mode 100644 index 0000000000..ee2840b5d3 --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/query-config.ts @@ -0,0 +1,19 @@ +export const defaultAdminShippingOptionTypeFields = [ + "id", + "label", + "code", + "description", + "created_at", + "updated_at", +] + +export const retrieveShippingOptionTypeTransformQueryConfig = { + defaults: defaultAdminShippingOptionTypeFields, + isList: false, +} + +export const listShippingOptionTypesTransformQueryConfig = { + ...retrieveShippingOptionTypeTransformQueryConfig, + defaultLimit: 20, + isList: true, +} diff --git a/packages/medusa/src/api/admin/shipping-option-types/route.ts b/packages/medusa/src/api/admin/shipping-option-types/route.ts new file mode 100644 index 0000000000..bd83d94651 --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/route.ts @@ -0,0 +1,49 @@ +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" + +import { ContainerRegistrationKeys } from "@medusajs/framework/utils" +import { createShippingOptionTypesWorkflow } from "@medusajs/core-flows" +import { refetchShippingOptionType } from "./helpers" +import { HttpTypes } from "@medusajs/framework/types" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) + + const { data: shippingOptionTypes, metadata } = await query.graph({ + entity: "shipping_option_type", + fields: req.queryConfig.fields, + filters: req.filterableFields, + pagination: req.queryConfig.pagination, + }) + + res.json({ + shipping_option_types: shippingOptionTypes, + count: metadata!.count, + offset: metadata!.skip, + limit: metadata!.take, + }) +} + +export const POST = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const input = [req.validatedBody] + + const { result } = await createShippingOptionTypesWorkflow(req.scope).run({ + input: { shipping_option_types: input }, + }) + + const shippingOptionType = await refetchShippingOptionType( + result[0].id, + req.scope, + req.queryConfig.fields + ) + + res.status(200).json({ shipping_option_type: shippingOptionType }) +} diff --git a/packages/medusa/src/api/admin/shipping-option-types/validators.ts b/packages/medusa/src/api/admin/shipping-option-types/validators.ts new file mode 100644 index 0000000000..167dd9c167 --- /dev/null +++ b/packages/medusa/src/api/admin/shipping-option-types/validators.ts @@ -0,0 +1,55 @@ +import { z } from "zod" +import { + createFindParams, + createOperatorMap, + createSelectParams, +} from "../../utils/validators" +import { applyAndAndOrOperators } from "../../utils/common-validators" + +export type AdminGetShippingOptionTypeParamsType = z.infer< + typeof AdminGetShippingOptionTypeParams +> +export const AdminGetShippingOptionTypeParams = createSelectParams() + +export const AdminGetShippingOptionTypesParamsFields = z.object({ + q: z.string().optional(), + id: z.union([z.string(), z.array(z.string())]).optional(), + label: z.union([z.string(), z.array(z.string())]).optional(), + code: z.union([z.string(), z.array(z.string())]).optional(), + description: z.union([z.string(), z.array(z.string())]).optional(), + created_at: createOperatorMap().optional(), + updated_at: createOperatorMap().optional(), + deleted_at: createOperatorMap().optional(), +}) + +export type AdminGetShippingOptionTypesParamsType = z.infer< + typeof AdminGetShippingOptionTypesParams +> +export const AdminGetShippingOptionTypesParams = createFindParams({ + limit: 10, + offset: 0, +}) + .merge(AdminGetShippingOptionTypesParamsFields) + .merge(applyAndAndOrOperators(AdminGetShippingOptionTypesParamsFields)) + +export type AdminCreateShippingOptionTypeType = z.infer< + typeof AdminCreateShippingOptionType +> +export const AdminCreateShippingOptionType = z + .object({ + label: z.string(), + code: z.string(), + description: z.string().optional(), + }) + .strict() + +export type AdminUpdateShippingOptionTypeType = z.infer< + typeof AdminUpdateShippingOptionType +> +export const AdminUpdateShippingOptionType = z + .object({ + label: z.string().optional(), + code: z.string().optional(), + description: z.string().optional(), + }) + .strict() diff --git a/packages/medusa/src/api/admin/shipping-options/validators.ts b/packages/medusa/src/api/admin/shipping-options/validators.ts index 61a8b59dc4..58438b64ef 100644 --- a/packages/medusa/src/api/admin/shipping-options/validators.ts +++ b/packages/medusa/src/api/admin/shipping-options/validators.ts @@ -79,7 +79,7 @@ export const AdminUpdateShippingOptionRule = z export const AdminCreateShippingOptionTypeObject = z .object({ label: z.string(), - description: z.string(), + description: z.string().optional(), code: z.string(), }) .strict() diff --git a/packages/medusa/src/api/middlewares.ts b/packages/medusa/src/api/middlewares.ts index 0d9a0b1884..3d6743c55c 100644 --- a/packages/medusa/src/api/middlewares.ts +++ b/packages/medusa/src/api/middlewares.ts @@ -60,6 +60,7 @@ import { storeProductRoutesMiddlewares } from "./store/products/middlewares" import { storeRegionRoutesMiddlewares } from "./store/regions/middlewares" import { storeReturnReasonRoutesMiddlewares } from "./store/return-reasons/middlewares" import { storeShippingOptionRoutesMiddlewares } from "./store/shipping-options/middlewares" +import { adminShippingOptionTypeRoutesMiddlewares } from "./admin/shipping-option-types/middlewares" export default defineMiddlewares([ ...storeRoutesMiddlewares, @@ -103,6 +104,7 @@ export default defineMiddlewares([ ...adminDraftOrderRoutesMiddlewares, ...adminSalesChannelRoutesMiddlewares, ...adminStockLocationRoutesMiddlewares, + ...adminShippingOptionTypeRoutesMiddlewares, ...adminProductTypeRoutesMiddlewares, ...adminProductTagRoutesMiddlewares, ...adminUploadRoutesMiddlewares, diff --git a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts index 824d0a836d..1ba946ec96 100644 --- a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts @@ -1642,6 +1642,90 @@ export default class FulfillmentModuleService return [...created, ...updated] } + async upsertShippingOptionTypes( + data: FulfillmentTypes.UpsertShippingOptionTypeDTO[], + sharedContext?: Context + ): Promise + async upsertShippingOptionTypes( + data: FulfillmentTypes.UpsertShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + @InjectTransactionManager() + async upsertShippingOptionTypes( + data: + | FulfillmentTypes.UpsertShippingOptionTypeDTO[] + | FulfillmentTypes.UpsertShippingOptionTypeDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise< + | FulfillmentTypes.ShippingOptionTypeDTO[] + | FulfillmentTypes.ShippingOptionTypeDTO + > { + const input = Array.isArray(data) ? data : [data] + + const results = await this.shippingOptionTypeService_.upsert( + input, + sharedContext + ) + + const allTypes = await this.baseRepository_.serialize< + | FulfillmentTypes.ShippingOptionTypeDTO[] + | FulfillmentTypes.ShippingOptionTypeDTO + >(results) + + return Array.isArray(data) ? allTypes : allTypes[0] + } + + // @ts-expect-error + updateShippingOptionTypes( + id: string, + data: FulfillmentTypes.UpdateShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + // @ts-expect-error + updateShippingOptionTypes( + selector: FulfillmentTypes.FilterableShippingOptionTypeProps, + data: FulfillmentTypes.UpdateShippingOptionTypeDTO, + sharedContext?: Context + ): Promise + + @InjectManager() + // @ts-expect-error + async updateShippingOptionTypes( + idOrSelector: string | FulfillmentTypes.FilterableShippingOptionTypeProps, + data: FulfillmentTypes.UpdateShippingOptionTypeDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + let normalizedInput: FulfillmentTypes.UpdateShippingOptionTypeDTO[] = [] + if (isString(idOrSelector)) { + // Check if the type exists in the first place + await this.shippingOptionTypeService_.retrieve(idOrSelector, {}, sharedContext) + normalizedInput = [{ id: idOrSelector, ...data }] + } else { + const types = await this.shippingOptionTypeService_.list( + idOrSelector, + {}, + sharedContext + ) + + normalizedInput = types.map((type) => ({ + id: type.id, + ...data, + })) + } + + const types = await this.shippingOptionTypeService_.update( + normalizedInput, + sharedContext + ) + + const updatedTypes = await this.baseRepository_.serialize< + FulfillmentTypes.ShippingOptionTypeDTO[] + >(types) + + return isString(idOrSelector) ? updatedTypes[0] : updatedTypes + } + // @ts-expect-error updateShippingProfiles( selector: FulfillmentTypes.FilterableShippingProfileProps,