fix(medusa): Shipping profile CRUD (#3154)
**What** - Fixes wrong payload class for `POST /admin/shipping-profiles` - Fixes wrong payload class for `POST /admin/shipping-profiles/:id` - Fixes an issue where updating a shipping profile with products and/or shipping options would fail. - Fixes an issue where passing `profile_id` to `ShippingOptionService.update()` would not update the shipping profile of the option. **Testing** - Adds new `simpleshippingProfileFactory` - Adds new integration test suite for shipping profiles operations. Resolves CORE-1065
This commit is contained in:
committed by
GitHub
parent
4d6e63d68f
commit
d0adaf57ed
@@ -10,6 +10,7 @@ describe("POST /admin/shipping-profiles", () => {
|
||||
subject = await request("POST", "/admin/shipping-profiles", {
|
||||
payload: {
|
||||
name: "Test Profile",
|
||||
type: "default",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
@@ -27,6 +28,7 @@ describe("POST /admin/shipping-profiles", () => {
|
||||
expect(ShippingProfileServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(ShippingProfileServiceMock.create).toHaveBeenCalledWith({
|
||||
name: "Test Profile",
|
||||
type: "default",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IsString } from "class-validator"
|
||||
import { IsEnum, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { ShippingProfileType } from "../../../../models"
|
||||
import { ShippingProfileService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
/**
|
||||
* @oas [post] /shipping-profiles
|
||||
@@ -84,12 +85,26 @@ export default async (req, res) => {
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* - type
|
||||
* properties:
|
||||
* name:
|
||||
* description: "The name of the Shipping Profile"
|
||||
* description: The name of the Shipping Profile
|
||||
* type: string
|
||||
* type:
|
||||
* description: The type of the Shipping Profile
|
||||
* type: string
|
||||
* enum: [default, gift_card, custom]
|
||||
*/
|
||||
export class AdminPostShippingProfilesReq {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsEnum(ShippingProfileType, {
|
||||
message: "type must be one of 'default', 'custom', 'gift_card'",
|
||||
})
|
||||
type: ShippingProfileType
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { IsOptional, IsString } from "class-validator"
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
|
||||
import { EntityManager } from "typeorm"
|
||||
import { ShippingProfileType } from "../../../../models"
|
||||
import { ShippingProfileService } from "../../../../services"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
/**
|
||||
* @oas [post] /shipping-profiles/{id}
|
||||
@@ -93,11 +100,44 @@ export default async (req, res) => {
|
||||
* type: object
|
||||
* properties:
|
||||
* name:
|
||||
* description: "The name of the Shipping Profile"
|
||||
* description: The name of the Shipping Profile
|
||||
* type: string
|
||||
* metadata:
|
||||
* description: An optional set of key-value pairs with additional information.
|
||||
* type: object
|
||||
* type:
|
||||
* description: The type of the Shipping Profile
|
||||
* type: string
|
||||
* enum: [default, gift_card, custom]
|
||||
* products:
|
||||
* description: An optional array of product ids to associate with the Shipping Profile
|
||||
* type: array
|
||||
* shipping_options:
|
||||
* description: An optional array of shipping option ids to associate with the Shipping Profile
|
||||
* type: array
|
||||
*/
|
||||
export class AdminPostShippingProfilesProfileReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsObject()
|
||||
metadata?: Record<string, unknown>
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(ShippingProfileType, {
|
||||
message: "type must be one of 'default', 'custom', 'gift_card'",
|
||||
})
|
||||
type?: ShippingProfileType
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
products?: string[]
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
shipping_options?: string[]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
In,
|
||||
Repository,
|
||||
} from "typeorm"
|
||||
import { PriceList, Product, SalesChannel, ProductCategory } from "../models"
|
||||
import { PriceList, Product, ProductCategory, SalesChannel } from "../models"
|
||||
import {
|
||||
ExtendedFindConfig,
|
||||
Selector,
|
||||
@@ -540,6 +540,25 @@ export class ProductRepository extends Repository<Product> {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Upserts shipping profile for products
|
||||
* @param productIds IDs of products to update
|
||||
* @param shippingProfileId ID of shipping profile to assign to products
|
||||
* @returns updated products
|
||||
*/
|
||||
public async upsertShippingProfile(
|
||||
productIds: string[],
|
||||
shippingProfileId: string
|
||||
): Promise<Product[]> {
|
||||
await this.createQueryBuilder()
|
||||
.update(Product)
|
||||
.set({ profile_id: shippingProfileId })
|
||||
.where({ id: In(productIds) })
|
||||
.execute()
|
||||
|
||||
return await this.findByIds(productIds)
|
||||
}
|
||||
|
||||
private _cleanOptions(
|
||||
options: FindWithoutRelationsOptions
|
||||
): WithRequiredProperty<FindWithoutRelationsOptions, "where"> {
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import { EntityRepository, Repository } from "typeorm"
|
||||
import { EntityRepository, In, Repository } from "typeorm"
|
||||
import { ShippingOption } from "../models/shipping-option"
|
||||
|
||||
@EntityRepository(ShippingOption)
|
||||
export class ShippingOptionRepository extends Repository<ShippingOption> {}
|
||||
export class ShippingOptionRepository extends Repository<ShippingOption> {
|
||||
public async upsertShippingProfile(
|
||||
shippingOptionIds: string[],
|
||||
shippingProfileId: string
|
||||
): Promise<ShippingOption[]> {
|
||||
await this.createQueryBuilder()
|
||||
.update(ShippingOption)
|
||||
.set({ profile_id: shippingProfileId })
|
||||
.where({ id: In(shippingOptionIds) })
|
||||
.execute()
|
||||
|
||||
return this.findByIds(shippingOptionIds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ export const ProductServiceMock = {
|
||||
deleteOption: jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve(products.productWithOptions)),
|
||||
updateshippingProfiles: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
retrieveVariants: jest.fn().mockImplementation((productId) => {
|
||||
if (productId === IdMap.getId("product1")) {
|
||||
return Promise.resolve([
|
||||
|
||||
@@ -89,6 +89,7 @@ export const ShippingOptionServiceMock = {
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
update: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
updateShippingprofile: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
listAndCount: jest.fn().mockImplementation((data) => {
|
||||
if (data.region_id === IdMap.getId("region-france")) {
|
||||
return Promise.resolve([[shippingOptions.franceShipping], 1])
|
||||
|
||||
@@ -19,10 +19,10 @@ export const ShippingProfileServiceMock = {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
update: jest.fn().mockImplementation(data => {
|
||||
update: jest.fn().mockImplementation((data) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
create: jest.fn().mockImplementation(data => {
|
||||
create: jest.fn().mockImplementation((data) => {
|
||||
return Promise.resolve(data)
|
||||
}),
|
||||
createDefault: jest.fn().mockImplementation(() => {
|
||||
@@ -31,7 +31,7 @@ export const ShippingProfileServiceMock = {
|
||||
createGiftCardDefault: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
retrieve: jest.fn().mockImplementation(data => {
|
||||
retrieve: jest.fn().mockImplementation((data) => {
|
||||
if (data === IdMap.getId("default")) {
|
||||
return Promise.resolve(profiles.default)
|
||||
}
|
||||
@@ -40,13 +40,13 @@ export const ShippingProfileServiceMock = {
|
||||
}
|
||||
return Promise.resolve(profiles.default)
|
||||
}),
|
||||
retrieveGiftCardDefault: jest.fn().mockImplementation(data => {
|
||||
retrieveGiftCardDefault: jest.fn().mockImplementation((data) => {
|
||||
return Promise.resolve({ id: IdMap.getId("giftCardProfile") })
|
||||
}),
|
||||
retrieveDefault: jest.fn().mockImplementation(data => {
|
||||
retrieveDefault: jest.fn().mockImplementation((data) => {
|
||||
return Promise.resolve({ id: IdMap.getId("default_shipping_profile") })
|
||||
}),
|
||||
list: jest.fn().mockImplementation(selector => {
|
||||
list: jest.fn().mockImplementation((selector) => {
|
||||
if (!selector) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export const ShippingProfileServiceMock = {
|
||||
])
|
||||
}
|
||||
}),
|
||||
decorate: jest.fn().mockImplementation(d => Promise.resolve(d)),
|
||||
decorate: jest.fn().mockImplementation((d) => Promise.resolve(d)),
|
||||
addShippingOption: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
removeShippingOption: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
addProduct: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
|
||||
@@ -35,14 +35,14 @@ describe("ShippingProfileService", () => {
|
||||
})
|
||||
|
||||
const productService = {
|
||||
update: jest.fn(),
|
||||
updateShippingProfile: jest.fn(),
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const shippingOptionService = {
|
||||
update: jest.fn(),
|
||||
updateShippingProfile: jest.fn(),
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
@@ -75,10 +75,11 @@ describe("ShippingProfileService", () => {
|
||||
products: [IdMap.getId("product1")],
|
||||
})
|
||||
|
||||
expect(productService.update).toBeCalledTimes(1)
|
||||
expect(productService.update).toBeCalledWith(IdMap.getId("product1"), {
|
||||
profile_id: id,
|
||||
})
|
||||
expect(productService.updateShippingProfile).toBeCalledTimes(1)
|
||||
expect(productService.updateShippingProfile).toBeCalledWith(
|
||||
[IdMap.getId("product1")],
|
||||
id
|
||||
)
|
||||
})
|
||||
|
||||
it("calls updateOne with shipping options", async () => {
|
||||
@@ -88,10 +89,10 @@ describe("ShippingProfileService", () => {
|
||||
shipping_options: [IdMap.getId("validId")],
|
||||
})
|
||||
|
||||
expect(shippingOptionService.update).toBeCalledTimes(1)
|
||||
expect(shippingOptionService.update).toBeCalledWith(
|
||||
IdMap.getId("validId"),
|
||||
{ profile_id: id }
|
||||
expect(shippingOptionService.updateShippingProfile).toBeCalledTimes(1)
|
||||
expect(shippingOptionService.updateShippingProfile).toBeCalledWith(
|
||||
[IdMap.getId("validId")],
|
||||
id
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -125,7 +126,7 @@ describe("ShippingProfileService", () => {
|
||||
const profRepo = MockRepository({ findOne: () => Promise.resolve({}) })
|
||||
|
||||
const productService = {
|
||||
update: jest.fn(),
|
||||
updateShippingProfile: jest.fn(),
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
@@ -142,15 +143,15 @@ describe("ShippingProfileService", () => {
|
||||
})
|
||||
|
||||
it("add product to profile successfully", async () => {
|
||||
await profileService.addProduct(
|
||||
IdMap.getId("validId"),
|
||||
IdMap.getId("product2")
|
||||
)
|
||||
await profileService.addProduct(IdMap.getId("validId"), [
|
||||
IdMap.getId("product2"),
|
||||
])
|
||||
|
||||
expect(productService.update).toBeCalledTimes(1)
|
||||
expect(productService.update).toBeCalledWith(IdMap.getId("product2"), {
|
||||
profile_id: IdMap.getId("validId"),
|
||||
})
|
||||
expect(productService.updateShippingProfile).toBeCalledTimes(1)
|
||||
expect(productService.updateShippingProfile).toBeCalledWith(
|
||||
[IdMap.getId("product2")],
|
||||
IdMap.getId("validId")
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -300,7 +301,7 @@ describe("ShippingProfileService", () => {
|
||||
const profRepo = MockRepository({ findOne: () => Promise.resolve({}) })
|
||||
|
||||
const shippingOptionService = {
|
||||
update: jest.fn(),
|
||||
updateShippingProfile: jest.fn(),
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
@@ -317,15 +318,14 @@ describe("ShippingProfileService", () => {
|
||||
})
|
||||
|
||||
it("add shipping option to profile successfully", async () => {
|
||||
await profileService.addShippingOption(
|
||||
IdMap.getId("validId"),
|
||||
IdMap.getId("freeShipping")
|
||||
)
|
||||
|
||||
expect(shippingOptionService.update).toBeCalledTimes(1)
|
||||
expect(shippingOptionService.update).toBeCalledWith(
|
||||
await profileService.addShippingOption(IdMap.getId("validId"), [
|
||||
IdMap.getId("freeShipping"),
|
||||
{ profile_id: IdMap.getId("validId") }
|
||||
])
|
||||
|
||||
expect(shippingOptionService.updateShippingProfile).toBeCalledTimes(1)
|
||||
expect(shippingOptionService.updateShippingProfile).toBeCalledWith(
|
||||
[IdMap.getId("freeShipping")],
|
||||
IdMap.getId("validId")
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { FlagRouter } from "../utils/flag-router"
|
||||
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager, In } from "typeorm"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { ProductVariantService, SearchService } from "."
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
|
||||
import {
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductOption,
|
||||
ProductTag,
|
||||
ProductType,
|
||||
ProductVariant,
|
||||
SalesChannel,
|
||||
ProductCategory,
|
||||
} from "../models"
|
||||
import { ImageRepository } from "../repositories/image"
|
||||
import {
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
ProductSelector,
|
||||
UpdateProductInput,
|
||||
} from "../types/product"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import { buildQuery, isString, setMetadata } from "../utils"
|
||||
import EventBusService from "./event-bus"
|
||||
|
||||
type InjectedDependencies = {
|
||||
@@ -446,7 +446,9 @@ class ProductService extends TransactionBaseService {
|
||||
|
||||
if (categories?.length) {
|
||||
const categoryIds = categories.map((c) => c.id)
|
||||
const categoryRecords = categoryIds.map((id) => ({ id } as ProductCategory))
|
||||
const categoryRecords = categoryIds.map(
|
||||
(id) => ({ id } as ProductCategory)
|
||||
)
|
||||
|
||||
product.categories = categoryRecords
|
||||
}
|
||||
@@ -560,7 +562,9 @@ class ProductService extends TransactionBaseService {
|
||||
|
||||
if (categories?.length) {
|
||||
const categoryIds = categories.map((c) => c.id)
|
||||
const categoryRecords = categoryIds.map((id) => ({ id } as ProductCategory))
|
||||
const categoryRecords = categoryIds.map(
|
||||
(id) => ({ id } as ProductCategory)
|
||||
)
|
||||
|
||||
product.categories = categoryRecords
|
||||
}
|
||||
@@ -869,6 +873,31 @@ class ProductService extends TransactionBaseService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param productIds ID or IDs of the products to update
|
||||
* @param profileId Shipping profile ID to update the shipping options with
|
||||
* @returns updated shipping options
|
||||
*/
|
||||
async updateShippingProfile(
|
||||
productIds: string | string[],
|
||||
profileId: string
|
||||
): Promise<Product[]> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const ids = isString(productIds) ? [productIds] : productIds
|
||||
|
||||
const products = await productRepo.upsertShippingProfile(ids, profileId)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(ProductService.Events.UPDATED, products)
|
||||
|
||||
return products
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query object to be used for list queries.
|
||||
* @param selector - the selector to create the query from
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
UpdateShippingOptionInput,
|
||||
ValidatePriceTypeAndAmountInput,
|
||||
} from "../types/shipping-options"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import { buildQuery, isString, setMetadata } from "../utils"
|
||||
import { FlagRouter } from "../utils/flag-router"
|
||||
import FulfillmentProviderService from "./fulfillment-provider"
|
||||
import RegionService from "./region"
|
||||
@@ -663,6 +663,10 @@ class ShippingOptionService extends TransactionBaseService {
|
||||
optionWithValidatedPrice.admin_only = update.admin_only
|
||||
}
|
||||
|
||||
if (isDefined(update.profile_id)) {
|
||||
optionWithValidatedPrice.profile_id = update.profile_id
|
||||
}
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
TaxInclusivePricingFeatureFlag.key
|
||||
@@ -754,6 +758,25 @@ class ShippingOptionService extends TransactionBaseService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param optionIds ID or IDs of the shipping options to update
|
||||
* @param profileId Shipping profile ID to update the shipping options with
|
||||
* @returns updated shipping options
|
||||
*/
|
||||
async updateShippingProfile(
|
||||
optionIds: string | string[],
|
||||
profileId: string
|
||||
): Promise<ShippingOption[]> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const optionRepo = manager.getCustomRepository(this.optionRepository_)
|
||||
|
||||
const ids = isString(optionIds) ? [optionIds] : optionIds
|
||||
|
||||
return await optionRepo.upsertShippingProfile(ids, profileId)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount to be paid for a shipping method. Will ask the
|
||||
* fulfillment provider to calculate the price if the shipping option has the
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
CreateShippingProfile,
|
||||
UpdateShippingProfile,
|
||||
} from "../types/shipping-profile"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import { buildQuery, isString, setMetadata } from "../utils"
|
||||
import CustomShippingOptionService from "./custom-shipping-option"
|
||||
import ProductService from "./product"
|
||||
import ShippingOptionService from "./shipping-option"
|
||||
@@ -260,7 +260,14 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const created = profileRepository.create(profile)
|
||||
const { metadata, ...rest } = profile
|
||||
|
||||
const created = profileRepository.create(rest)
|
||||
|
||||
if (metadata) {
|
||||
created.metadata = setMetadata(created, metadata)
|
||||
}
|
||||
|
||||
const result = await profileRepository.save(created)
|
||||
return result
|
||||
})
|
||||
@@ -284,7 +291,7 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
this.shippingProfileRepository_
|
||||
)
|
||||
|
||||
const profile = await this.retrieve(profileId, {
|
||||
let profile = await this.retrieve(profileId, {
|
||||
relations: [
|
||||
"products",
|
||||
"products.profile",
|
||||
@@ -295,27 +302,16 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
|
||||
const { metadata, products, shipping_options, ...rest } = update
|
||||
|
||||
if (metadata) {
|
||||
profile.metadata = setMetadata(profile, metadata)
|
||||
}
|
||||
|
||||
if (products) {
|
||||
const productServiceTx = this.productService_.withTransaction(manager)
|
||||
for (const pId of products) {
|
||||
await productServiceTx.update(pId, {
|
||||
profile_id: profile.id,
|
||||
})
|
||||
}
|
||||
profile = await this.addProduct(profile.id, products)
|
||||
}
|
||||
|
||||
if (shipping_options) {
|
||||
const shippingOptionServiceTx =
|
||||
this.shippingOptionService_.withTransaction(manager)
|
||||
for (const oId of shipping_options) {
|
||||
await shippingOptionServiceTx.update(oId, {
|
||||
profile_id: profile.id,
|
||||
})
|
||||
}
|
||||
profile = await this.addShippingOption(profile.id, shipping_options)
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
profile.metadata = setMetadata(profile, metadata)
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(rest)) {
|
||||
@@ -352,22 +348,31 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a product to a profile. The method is idempotent, so multiple calls
|
||||
* with the same product variant will have the same result.
|
||||
* @param profileId - the profile to add the product to.
|
||||
* @param productId - the product to add.
|
||||
* Adds a product of an array of products to the profile.
|
||||
* @param profileId - the profile to add the products to.
|
||||
* @param productId - the ID of the product or multiple products to add.
|
||||
* @return the result of update
|
||||
*/
|
||||
async addProduct(
|
||||
profileId: string,
|
||||
productId: string
|
||||
productId: string | string[]
|
||||
): Promise<ShippingProfile> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
await this.productService_
|
||||
.withTransaction(manager)
|
||||
.update(productId, { profile_id: profileId })
|
||||
const productServiceTx = this.productService_.withTransaction(manager)
|
||||
|
||||
return await this.retrieve(profileId)
|
||||
await productServiceTx.updateShippingProfile(
|
||||
isString(productId) ? [productId] : productId,
|
||||
profileId
|
||||
)
|
||||
|
||||
return await this.retrieve(profileId, {
|
||||
relations: [
|
||||
"products",
|
||||
"products.profile",
|
||||
"shipping_options",
|
||||
"shipping_options.profile",
|
||||
],
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -375,20 +380,30 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
* Adds a shipping option to the profile. The shipping option can be used to
|
||||
* fulfill the products in the products field.
|
||||
* @param profileId - the profile to apply the shipping option to
|
||||
* @param optionId - the option to add to the profile
|
||||
* @param optionId - the ID of the option or multiple options to add to the profile
|
||||
* @return the result of the model update operation
|
||||
*/
|
||||
async addShippingOption(
|
||||
profileId: string,
|
||||
optionId: string
|
||||
optionId: string | string[]
|
||||
): Promise<ShippingProfile> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
await this.shippingOptionService_
|
||||
.withTransaction(manager)
|
||||
.update(optionId, { profile_id: profileId })
|
||||
const shippingOptionServiceTx =
|
||||
this.shippingOptionService_.withTransaction(manager)
|
||||
|
||||
const updated = await this.retrieve(profileId)
|
||||
return updated
|
||||
await shippingOptionServiceTx.updateShippingProfile(
|
||||
isString(optionId) ? [optionId] : optionId,
|
||||
profileId
|
||||
)
|
||||
|
||||
return await this.retrieve(profileId, {
|
||||
relations: [
|
||||
"products",
|
||||
"products.profile",
|
||||
"shipping_options",
|
||||
"shipping_options.profile",
|
||||
],
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Product, ShippingOption, ShippingProfileType } from "../models"
|
||||
import { ShippingProfileType } from "../models"
|
||||
|
||||
export type CreateShippingProfile = {
|
||||
name: string
|
||||
type: ShippingProfileType
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type UpdateShippingProfile = {
|
||||
|
||||
Reference in New Issue
Block a user