feat(medusa,fulfillment): pass stock location data to fulfillment provider (#9322)
**What** - Fetches the stock location's details when creating a fulfillment and return fulfillment. - Passes the data to the fulfillment module, which in turn passes it to the fulfillment provider. **Why** - When creating a fulfillment in a multi-location setup the fulfillment provider will need to know where the package is being sent from (so the shipping service can pick it up). - Previously, we didn't pass anything but the location id to the fulfillment provider. Because the fulfillment provider can't have a dependency on the stock location module this was not sufficient. - This change ensures there is enough data passed to the fulfillment provider to build integrations properly.
This commit is contained in:
@@ -9,12 +9,13 @@ export function generateCreateFulfillmentData(
|
||||
provider_id: string
|
||||
shipping_option_id: string
|
||||
order_id: string
|
||||
location_id: string
|
||||
}
|
||||
) {
|
||||
const randomString = Math.random().toString(36).substring(7)
|
||||
|
||||
return {
|
||||
location_id: "test-location",
|
||||
location_id: data.location_id,
|
||||
packed_at: null,
|
||||
shipped_at: null,
|
||||
delivered_at: null,
|
||||
@@ -97,8 +98,10 @@ export async function setupFullDataFulfillmentStructure(
|
||||
service: IFulfillmentModuleService,
|
||||
{
|
||||
providerId,
|
||||
locationId,
|
||||
}: {
|
||||
providerId: string
|
||||
locationId: string
|
||||
}
|
||||
) {
|
||||
const randomString = Math.random().toString(36).substring(7)
|
||||
@@ -133,6 +136,8 @@ export async function setupFullDataFulfillmentStructure(
|
||||
|
||||
await service.createFulfillment(
|
||||
generateCreateFulfillmentData({
|
||||
order_id: "fake-order",
|
||||
location_id: locationId,
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
})
|
||||
|
||||
@@ -6,8 +6,12 @@ import {
|
||||
updateFulfillmentWorkflow,
|
||||
updateFulfillmentWorkflowId,
|
||||
} from "@medusajs/core-flows"
|
||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/utils"
|
||||
import {
|
||||
IFulfillmentModuleService,
|
||||
MedusaContainer,
|
||||
StockLocationDTO,
|
||||
} from "@medusajs/types"
|
||||
import { ContainerRegistrationKeys, Modules } from "@medusajs/utils"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
import {
|
||||
generateCreateFulfillmentData,
|
||||
@@ -22,7 +26,8 @@ medusaIntegrationTestRunner({
|
||||
env: { MEDUSA_FF_MEDUSA_V2: true },
|
||||
testSuite: ({ getContainer }) => {
|
||||
describe("Workflows: Fulfillment", () => {
|
||||
let appContainer
|
||||
let location: StockLocationDTO
|
||||
let appContainer: MedusaContainer
|
||||
let service: IFulfillmentModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -30,7 +35,101 @@ medusaIntegrationTestRunner({
|
||||
service = appContainer.resolve(Modules.FULFILLMENT)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
const stockLocationService = appContainer.resolve(
|
||||
Modules.STOCK_LOCATION
|
||||
)
|
||||
|
||||
location = await stockLocationService.createStockLocations({
|
||||
name: "Test Location",
|
||||
address: {
|
||||
address_1: "Test Address",
|
||||
address_2: "tttest",
|
||||
city: "Test City",
|
||||
country_code: "us",
|
||||
postal_code: "12345",
|
||||
metadata: { email: "test@mail.com" },
|
||||
},
|
||||
metadata: { custom_location: "yes" },
|
||||
})
|
||||
})
|
||||
|
||||
describe("createFulfillmentWorkflow", () => {
|
||||
describe("invoke", () => {
|
||||
it("should get stock location", async () => {
|
||||
const workflow = createFulfillmentWorkflow(appContainer)
|
||||
|
||||
const link = appContainer.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
|
||||
const shippingProfile = await service.createShippingProfiles({
|
||||
name: "test",
|
||||
type: "default",
|
||||
})
|
||||
|
||||
const fulfillmentSet = await service.createFulfillmentSets({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
})
|
||||
|
||||
await link.create({
|
||||
[Modules.STOCK_LOCATION]: {
|
||||
stock_location_id: location.id,
|
||||
},
|
||||
[Modules.FULFILLMENT]: {
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
},
|
||||
})
|
||||
|
||||
const serviceZone = await service.createServiceZones({
|
||||
name: "test",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
})
|
||||
|
||||
const shippingOption = await service.createShippingOptions(
|
||||
generateCreateShippingOptionsData({
|
||||
provider_id: providerId,
|
||||
service_zone_id: serviceZone.id,
|
||||
shipping_profile_id: shippingProfile.id,
|
||||
})
|
||||
)
|
||||
|
||||
const data = generateCreateFulfillmentData({
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
order_id: "fake-order",
|
||||
location_id: location.id,
|
||||
})
|
||||
|
||||
const { transaction } = await workflow.run({
|
||||
input: data,
|
||||
throwOnError: true,
|
||||
})
|
||||
|
||||
expect(
|
||||
transaction.context.invoke["get-location"].output.output
|
||||
).toEqual({
|
||||
id: expect.any(String),
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
name: "Test Location",
|
||||
address: {
|
||||
id: expect.any(String),
|
||||
address_1: "Test Address",
|
||||
address_2: "tttest",
|
||||
city: "Test City",
|
||||
country_code: "us",
|
||||
postal_code: "12345",
|
||||
metadata: { email: "test@mail.com" },
|
||||
phone: null,
|
||||
province: null,
|
||||
},
|
||||
metadata: { custom_location: "yes" },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("compensation", () => {
|
||||
it("should cancel created fulfillment if step following step throws error", async () => {
|
||||
const workflow = createFulfillmentWorkflow(appContainer)
|
||||
@@ -70,6 +169,7 @@ medusaIntegrationTestRunner({
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
order_id: "fake-order",
|
||||
location_id: location.id,
|
||||
})
|
||||
const { errors } = await workflow.run({
|
||||
input: data,
|
||||
@@ -130,11 +230,16 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
|
||||
const data = generateCreateFulfillmentData({
|
||||
order_id: "fake-order",
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
location_id: location.id,
|
||||
})
|
||||
|
||||
const fulfillment = await service.createFulfillment(data)
|
||||
const fulfillment = await service.createFulfillment({
|
||||
...data,
|
||||
location,
|
||||
})
|
||||
|
||||
const date = new Date()
|
||||
const { errors } = await workflow.run({
|
||||
@@ -142,7 +247,7 @@ medusaIntegrationTestRunner({
|
||||
id: fulfillment.id,
|
||||
shipped_at: date,
|
||||
packed_at: date,
|
||||
location_id: "new location",
|
||||
location_id: location.id,
|
||||
},
|
||||
throwOnError: false,
|
||||
})
|
||||
@@ -209,12 +314,15 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
|
||||
const data = generateCreateFulfillmentData({
|
||||
order_id: "fake-order",
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
location_id: location.id,
|
||||
})
|
||||
|
||||
const fulfillment = await service.createFulfillment({
|
||||
...data,
|
||||
location,
|
||||
labels: [],
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
||||
import { IFulfillmentModuleService, StockLocationDTO } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/utils"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { createAdminUser } from "../../../helpers/create-admin-user"
|
||||
@@ -21,6 +21,7 @@ medusaIntegrationTestRunner({
|
||||
testSuite: ({ getContainer, api, dbConnection }) => {
|
||||
let service: IFulfillmentModuleService
|
||||
let container
|
||||
let location: StockLocationDTO
|
||||
|
||||
beforeAll(() => {
|
||||
container = getContainer()
|
||||
@@ -29,6 +30,20 @@ medusaIntegrationTestRunner({
|
||||
|
||||
beforeEach(async () => {
|
||||
await createAdminUser(dbConnection, adminHeaders, container)
|
||||
const stockLocationService = container.resolve(Modules.STOCK_LOCATION)
|
||||
|
||||
location = await stockLocationService.createStockLocations({
|
||||
name: "Test Location",
|
||||
address: {
|
||||
address_1: "Test Address",
|
||||
address_2: "tttest",
|
||||
city: "Test City",
|
||||
country_code: "us",
|
||||
postal_code: "12345",
|
||||
metadata: { email: "test@mail.com" },
|
||||
},
|
||||
metadata: { custom_location: "yes" },
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -38,7 +53,10 @@ medusaIntegrationTestRunner({
|
||||
*/
|
||||
describe("Fulfillment module migrations backward compatibility", () => {
|
||||
it("should allow to create a full data structure after the backward compatible migration have run on top of the medusa v1 database", async () => {
|
||||
await setupFullDataFulfillmentStructure(service, { providerId })
|
||||
await setupFullDataFulfillmentStructure(service, {
|
||||
providerId,
|
||||
locationId: location.id,
|
||||
})
|
||||
|
||||
const fulfillmentSets = await service.listFulfillmentSets(
|
||||
{},
|
||||
@@ -92,7 +110,10 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should cancel a fulfillment", async () => {
|
||||
await setupFullDataFulfillmentStructure(service, { providerId })
|
||||
await setupFullDataFulfillmentStructure(service, {
|
||||
providerId,
|
||||
locationId: location.id,
|
||||
})
|
||||
|
||||
const [fulfillment] = await service.listFulfillments()
|
||||
|
||||
@@ -138,6 +159,7 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
|
||||
const data = generateCreateFulfillmentData({
|
||||
location_id: location.id,
|
||||
provider_id: providerId,
|
||||
shipping_option_id: shippingOption.id,
|
||||
order_id: "order_123",
|
||||
@@ -151,7 +173,7 @@ medusaIntegrationTestRunner({
|
||||
expect(response.data.fulfillment).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
location_id: "test-location",
|
||||
location_id: location.id,
|
||||
packed_at: null,
|
||||
shipped_at: null,
|
||||
delivered_at: null,
|
||||
@@ -218,7 +240,10 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should update a fulfillment to be shipped", async () => {
|
||||
await setupFullDataFulfillmentStructure(service, { providerId })
|
||||
await setupFullDataFulfillmentStructure(service, {
|
||||
providerId,
|
||||
locationId: location.id,
|
||||
})
|
||||
|
||||
const [fulfillment] = await service.listFulfillments()
|
||||
|
||||
@@ -255,7 +280,10 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
it("should throw error when already shipped", async () => {
|
||||
await setupFullDataFulfillmentStructure(service, { providerId })
|
||||
await setupFullDataFulfillmentStructure(service, {
|
||||
providerId,
|
||||
locationId: location.id,
|
||||
})
|
||||
|
||||
const [fulfillment] = await service.listFulfillments()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user