feat(medusa,orchestration): Decouple Product in Cart domain (#4945)
This commit is contained in:
committed by
GitHub
parent
1958809fa9
commit
4b0e3fb2a7
@@ -556,6 +556,7 @@ describe("/store/carts", () => {
|
||||
{
|
||||
id: "test-li",
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
quantity: 1,
|
||||
unit_price: 100,
|
||||
adjustments: [
|
||||
@@ -644,6 +645,7 @@ describe("/store/carts", () => {
|
||||
id: "line-item-2",
|
||||
cart_id: discountCart.id,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
unit_price: 950,
|
||||
quantity: 1,
|
||||
adjustments: [
|
||||
@@ -713,6 +715,7 @@ describe("/store/carts", () => {
|
||||
id: "line-item-2",
|
||||
cart_id: discountCart.id,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
adjustments: [
|
||||
@@ -804,6 +807,7 @@ describe("/store/carts", () => {
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-w-total-fixed-discount",
|
||||
})
|
||||
|
||||
@@ -847,6 +851,7 @@ describe("/store/carts", () => {
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-w-total-percentage-discount",
|
||||
})
|
||||
|
||||
@@ -890,6 +895,7 @@ describe("/store/carts", () => {
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-w-item-fixed-discount",
|
||||
})
|
||||
|
||||
@@ -933,6 +939,7 @@ describe("/store/carts", () => {
|
||||
unit_price: 1000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-quantity",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-w-item-percentage-discount",
|
||||
})
|
||||
|
||||
@@ -1050,6 +1057,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -1110,6 +1118,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -1260,6 +1269,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -1327,6 +1337,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -1387,6 +1398,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -1457,6 +1469,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
unit_price: 100,
|
||||
},
|
||||
],
|
||||
@@ -2207,6 +2220,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
unit_price: 1000,
|
||||
},
|
||||
@@ -2251,6 +2265,7 @@ describe("/store/carts", () => {
|
||||
line_items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
unit_price: 1000,
|
||||
},
|
||||
@@ -2702,7 +2717,13 @@ describe("/store/carts", () => {
|
||||
const product = await simpleProductFactory(dbConnection)
|
||||
const cart = await simpleCartFactory(dbConnection, {
|
||||
region: region.id,
|
||||
line_items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||
line_items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
shipping_address: {
|
||||
country_code: "us",
|
||||
},
|
||||
@@ -2737,7 +2758,13 @@ describe("/store/carts", () => {
|
||||
const product = await simpleProductFactory(dbConnection)
|
||||
const cart = await simpleCartFactory(dbConnection, {
|
||||
region: region.id,
|
||||
line_items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||
line_items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
await simpleShippingOptionFactory(dbConnection, {
|
||||
region_id: region.id,
|
||||
@@ -2769,7 +2796,13 @@ describe("/store/carts", () => {
|
||||
const product = await simpleProductFactory(dbConnection)
|
||||
const cart = await simpleCartFactory(dbConnection, {
|
||||
region: region.id,
|
||||
line_items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||
line_items: [
|
||||
{
|
||||
variant_id: product.variants[0].id,
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
await simpleShippingOptionFactory(dbConnection, {
|
||||
region_id: region.id,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataSource } from "typeorm"
|
||||
import faker from "faker"
|
||||
import { LineItem, LineItemAdjustment, LineItemTaxLine } from "@medusajs/medusa"
|
||||
import faker from "faker"
|
||||
import { DataSource } from "typeorm"
|
||||
|
||||
type TaxLineFactoryData = {
|
||||
rate: number
|
||||
@@ -18,6 +18,7 @@ export type LineItemFactoryData = {
|
||||
cart_id?: string
|
||||
order_id?: string
|
||||
variant_id: string | null
|
||||
product_id: string | null
|
||||
title?: string
|
||||
description?: string
|
||||
thumbnail?: string
|
||||
@@ -75,7 +76,8 @@ export const simpleLineItemFactory = async (
|
||||
adjustments: data.adjustments,
|
||||
includes_tax: data.includes_tax,
|
||||
order_edit_id: data.order_edit_id,
|
||||
is_giftcard: data.is_giftcard || false
|
||||
is_giftcard: data.is_giftcard || false,
|
||||
product_id: data.product_id,
|
||||
})
|
||||
|
||||
const line = await manager.save(toSave)
|
||||
|
||||
@@ -834,6 +834,7 @@ module.exports = async (dataSource, data = {}) => {
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-2",
|
||||
})
|
||||
await manager.save(li)
|
||||
@@ -887,6 +888,7 @@ module.exports = async (dataSource, data = {}) => {
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
product_id: "test-product",
|
||||
cart_id: "test-cart-3",
|
||||
})
|
||||
await manager.save(li2)
|
||||
@@ -951,6 +953,7 @@ module.exports = async (dataSource, data = {}) => {
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-sale-cg",
|
||||
cart_id: "test-cart-3",
|
||||
product_id: "test-product",
|
||||
metadata: { "some-existing": "prop" },
|
||||
})
|
||||
await manager.save(li3)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {
|
||||
CartService,
|
||||
DraftOrderService,
|
||||
LineItemService,
|
||||
} from "../../../../services"
|
||||
import { IsInt, IsObject, IsOptional, IsString } from "class-validator"
|
||||
import {
|
||||
defaultAdminDraftOrdersCartFields,
|
||||
defaultAdminDraftOrdersCartRelations,
|
||||
defaultAdminDraftOrdersFields,
|
||||
} from "."
|
||||
import {
|
||||
CartService,
|
||||
DraftOrderService,
|
||||
LineItemService,
|
||||
} from "../../../../services"
|
||||
|
||||
import { EntityManager } from "typeorm"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { cleanResponseData } from "../../../../utils/clean-response-data"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /admin/draft-orders/{id}/line-items
|
||||
@@ -119,7 +119,9 @@ export default async (req, res) => {
|
||||
|
||||
await cartService
|
||||
.withTransaction(manager)
|
||||
.addLineItem(draftOrder.cart_id, line, { validateSalesChannels: false })
|
||||
.addOrUpdateLineItems(draftOrder.cart_id, line, {
|
||||
validateSalesChannels: false,
|
||||
})
|
||||
} else {
|
||||
// custom line items can be added to a draft order
|
||||
await lineItemService.withTransaction(manager).create({
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function handleAddOrUpdateLineItem(
|
||||
metadata: data.metadata,
|
||||
})
|
||||
|
||||
await txCartService.addLineItem(cart.id, line, {
|
||||
await txCartService.addOrUpdateLineItems(cart.id, line, {
|
||||
validateSalesChannels: featureFlagRouter.isFeatureEnabled("sales_channels"),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { IsBooleanString, IsOptional, IsString } from "class-validator"
|
||||
import { PricingService, ProductService } from "../../../../services"
|
||||
import { defaultRelations } from "."
|
||||
import IsolateProductDomainFeatureFlag from "../../../../loaders/feature-flags/isolate-product-domain"
|
||||
import {
|
||||
PricingService,
|
||||
ProductService,
|
||||
ShippingProfileService,
|
||||
} from "../../../../services"
|
||||
import ShippingOptionService from "../../../../services/shipping-option"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { defaultRelations } from "."
|
||||
|
||||
/**
|
||||
* @oas [get] /store/shipping-options
|
||||
@@ -61,6 +67,10 @@ export default async (req, res) => {
|
||||
const shippingOptionService: ShippingOptionService = req.scope.resolve(
|
||||
"shippingOptionService"
|
||||
)
|
||||
const shippingProfileService: ShippingProfileService = req.scope.resolve(
|
||||
"shippingProfileService"
|
||||
)
|
||||
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
|
||||
|
||||
// should be selector
|
||||
const query: Record<string, unknown> = {}
|
||||
@@ -76,8 +86,17 @@ export default async (req, res) => {
|
||||
query.admin_only = false
|
||||
|
||||
if (productIds.length) {
|
||||
const prods = await productService.list({ id: productIds })
|
||||
query.profile_id = prods.map((p) => p.profile_id)
|
||||
if (
|
||||
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)
|
||||
) {
|
||||
const productShippinProfileMap =
|
||||
await shippingProfileService.getMapProfileIdsByProductIds(productIds)
|
||||
|
||||
query.profile_id = [...productShippinProfileMap.values()]
|
||||
} else {
|
||||
const prods = await productService.list({ id: productIds })
|
||||
query.profile_id = prods.map((p) => p.profile_id)
|
||||
}
|
||||
}
|
||||
|
||||
const options = await shippingOptionService.list(query, {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
import IsolateProductDomain from "../loaders/feature-flags/isolate-product-domain"
|
||||
|
||||
export const featureFlag = IsolateProductDomain.key
|
||||
|
||||
export class LineItemProductId1692870898424 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "line_item" DROP CONSTRAINT IF EXISTS "FK_5371cbaa3be5200f373d24e3d5b";
|
||||
ALTER TABLE "line_item" ADD COLUMN IF NOT EXISTS "product_id" text;
|
||||
|
||||
UPDATE "line_item" SET "product_id" = pv."product_id"
|
||||
FROM "product_variant" pv
|
||||
WHERE "line_item"."variant_id" = "pv"."id";
|
||||
`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE "line_item" DROP COLUMN IF EXISTS "product_id";
|
||||
ALTER TABLE "line_item" ADD CONSTRAINT "FK_5371cbaa3be5200f373d24e3d5b" FOREIGN KEY ("variant_id") REFERENCES "product_variant" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
`)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import {
|
||||
AfterLoad,
|
||||
AfterUpdate,
|
||||
BeforeInsert,
|
||||
BeforeUpdate,
|
||||
Check,
|
||||
Column,
|
||||
Entity,
|
||||
@@ -11,9 +14,11 @@ import {
|
||||
|
||||
import { BaseEntity } from "../interfaces"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { generateEntityId } from "../utils"
|
||||
import { DbAwareColumn } from "../utils/db-aware-column"
|
||||
import { FeatureFlagColumn } from "../utils/feature-flag-decorators"
|
||||
import { DbAwareColumn, generateEntityId } from "../utils"
|
||||
import {
|
||||
FeatureFlagColumn,
|
||||
FeatureFlagDecorators,
|
||||
} from "../utils/feature-flag-decorators"
|
||||
import { Cart } from "./cart"
|
||||
import { ClaimOrder } from "./claim-order"
|
||||
import { LineItemAdjustment } from "./line-item-adjustment"
|
||||
@@ -22,6 +27,8 @@ import { Order } from "./order"
|
||||
import { OrderEdit } from "./order-edit"
|
||||
import { ProductVariant } from "./product-variant"
|
||||
import { Swap } from "./swap"
|
||||
import IsolateProductDomain from "../loaders/feature-flags/isolate-product-domain"
|
||||
import { featureFlagRouter } from "../loaders/feature-flags"
|
||||
|
||||
@Check(`"fulfilled_quantity" <= "quantity"`)
|
||||
@Check(`"shipped_quantity" <= "fulfilled_quantity"`)
|
||||
@@ -124,6 +131,9 @@ export class LineItem extends BaseEntity {
|
||||
@JoinColumn({ name: "variant_id" })
|
||||
variant: ProductVariant
|
||||
|
||||
@FeatureFlagColumn(IsolateProductDomain.key, { nullable: true, type: "text" })
|
||||
product_id: string | null
|
||||
|
||||
@Column({ type: "int" })
|
||||
quantity: number
|
||||
|
||||
@@ -155,6 +165,39 @@ export class LineItem extends BaseEntity {
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "item")
|
||||
|
||||
// This is to maintain compatibility while isolating the product domain
|
||||
if (featureFlagRouter.isFeatureEnabled(IsolateProductDomain.key)) {
|
||||
if (
|
||||
this.variant &&
|
||||
Object.keys(this.variant).length === 1 &&
|
||||
this.variant.product_id
|
||||
) {
|
||||
this.variant = undefined as any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FeatureFlagDecorators(IsolateProductDomain.key, [BeforeUpdate])
|
||||
beforeUpdate(): void {
|
||||
if (
|
||||
this.variant &&
|
||||
Object.keys(this.variant).length === 1 &&
|
||||
this.variant.product_id
|
||||
) {
|
||||
this.variant = undefined as any
|
||||
}
|
||||
}
|
||||
|
||||
@FeatureFlagDecorators(IsolateProductDomain.key, [AfterLoad, AfterUpdate])
|
||||
afterUpdateOrLoad(): void {
|
||||
if (this.variant) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.product_id) {
|
||||
this.variant = { product_id: this.product_id } as any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||
import { DeleteResult, EntityTarget, In, Not } from "typeorm"
|
||||
import { dataSource } from "../loaders/database"
|
||||
import { featureFlagRouter } from "../loaders/feature-flags"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import {
|
||||
Discount,
|
||||
DiscountCondition,
|
||||
@@ -11,7 +15,6 @@ import {
|
||||
DiscountConditionType,
|
||||
} from "../models"
|
||||
import { isString } from "../utils"
|
||||
import { dataSource } from "../loaders/database"
|
||||
|
||||
export enum DiscountConditionJoinTableForeignKey {
|
||||
PRODUCT_ID = "product_id",
|
||||
@@ -53,6 +56,7 @@ export const DiscountConditionRepository = dataSource
|
||||
joinTableForeignKey: DiscountConditionJoinTableForeignKey
|
||||
conditionTable: DiscountConditionResourceType
|
||||
joinTableKey: string
|
||||
relatedTable: string
|
||||
} {
|
||||
let conditionTable: DiscountConditionResourceType =
|
||||
DiscountConditionProduct
|
||||
@@ -61,6 +65,7 @@ export const DiscountConditionRepository = dataSource
|
||||
let joinTableForeignKey: DiscountConditionJoinTableForeignKey =
|
||||
DiscountConditionJoinTableForeignKey.PRODUCT_ID
|
||||
let joinTableKey = "id"
|
||||
let relatedTable = ""
|
||||
|
||||
// On the joined table (e.g. `product`), what key should be match on
|
||||
// (e.g `type_id` for product types and `id` for products)
|
||||
@@ -80,6 +85,7 @@ export const DiscountConditionRepository = dataSource
|
||||
joinTableForeignKey =
|
||||
DiscountConditionJoinTableForeignKey.PRODUCT_TYPE_ID
|
||||
joinTable = "product"
|
||||
relatedTable = "types"
|
||||
|
||||
conditionTable = DiscountConditionProductType
|
||||
break
|
||||
@@ -89,6 +95,7 @@ export const DiscountConditionRepository = dataSource
|
||||
joinTableForeignKey =
|
||||
DiscountConditionJoinTableForeignKey.PRODUCT_COLLECTION_ID
|
||||
joinTable = "product"
|
||||
relatedTable = "collections"
|
||||
|
||||
conditionTable = DiscountConditionProductCollection
|
||||
break
|
||||
@@ -99,6 +106,7 @@ export const DiscountConditionRepository = dataSource
|
||||
joinTableForeignKey =
|
||||
DiscountConditionJoinTableForeignKey.PRODUCT_TAG_ID
|
||||
joinTable = "product_tags"
|
||||
relatedTable = "tags"
|
||||
|
||||
conditionTable = DiscountConditionProductTag
|
||||
break
|
||||
@@ -123,6 +131,7 @@ export const DiscountConditionRepository = dataSource
|
||||
resourceKey,
|
||||
joinTableForeignKey,
|
||||
conditionTable,
|
||||
relatedTable,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -204,15 +213,56 @@ export const DiscountConditionRepository = dataSource
|
||||
.getMany()
|
||||
},
|
||||
|
||||
async queryConditionTable({ type, condId, resourceId }): Promise<number> {
|
||||
async queryConditionTable({
|
||||
type,
|
||||
conditionId,
|
||||
resourceId,
|
||||
}): Promise<number> {
|
||||
const {
|
||||
conditionTable,
|
||||
joinTable,
|
||||
joinTableForeignKey,
|
||||
resourceKey,
|
||||
joinTableKey,
|
||||
relatedTable,
|
||||
} = this.getJoinTableResourceIdentifiers(type)
|
||||
|
||||
if (
|
||||
type !== DiscountConditionType.CUSTOMER_GROUPS &&
|
||||
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)
|
||||
) {
|
||||
const module = MedusaModule.getModuleInstance(Modules.PRODUCT)[
|
||||
Modules.PRODUCT
|
||||
]
|
||||
const prop = relatedTable
|
||||
const resource = await module.retrieve(resourceId, {
|
||||
select: [`${prop ? prop + "." : ""}id`],
|
||||
relations: prop ? [prop] : [],
|
||||
})
|
||||
if (!resource) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const relatedResourceIds = prop
|
||||
? resource[prop].map((relatedResource) => relatedResource.id)
|
||||
: [resource.id]
|
||||
|
||||
if (!relatedResourceIds.length) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return await this.manager
|
||||
.createQueryBuilder(conditionTable, "dc")
|
||||
.where(
|
||||
`dc.condition_id = :conditionId AND dc.${joinTableForeignKey} IN (:...relatedResourceIds)`,
|
||||
{
|
||||
conditionId,
|
||||
relatedResourceIds,
|
||||
}
|
||||
)
|
||||
.getCount()
|
||||
}
|
||||
|
||||
return await this.manager
|
||||
.createQueryBuilder(conditionTable, "dc")
|
||||
.innerJoin(
|
||||
@@ -224,7 +274,7 @@ export const DiscountConditionRepository = dataSource
|
||||
}
|
||||
)
|
||||
.where(`dc.condition_id = :conditionId`, {
|
||||
conditionId: condId,
|
||||
conditionId,
|
||||
})
|
||||
.getCount()
|
||||
},
|
||||
@@ -258,7 +308,7 @@ export const DiscountConditionRepository = dataSource
|
||||
|
||||
const numConditions = await this.queryConditionTable({
|
||||
type: condition.type,
|
||||
condId: condition.id,
|
||||
conditionId: condition.id,
|
||||
resourceId: productId,
|
||||
})
|
||||
|
||||
@@ -307,7 +357,7 @@ export const DiscountConditionRepository = dataSource
|
||||
for (const condition of discountConditions) {
|
||||
const numConditions = await this.queryConditionTable({
|
||||
type: "customer_groups",
|
||||
condId: condition.id,
|
||||
conditionId: condition.id,
|
||||
resourceId: customerId,
|
||||
})
|
||||
|
||||
|
||||
@@ -143,9 +143,6 @@ export const ShippingProfileServiceMock = {
|
||||
fetchCartOptions: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve([{ id: IdMap.getId("cartShippingOption") }])
|
||||
}),
|
||||
fetchOptionsByProductIds: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve([{ id: IdMap.getId("cartShippingOption") }])
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import _ from "lodash"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { asClass, asValue, createContainer } from "awilix"
|
||||
import _ from "lodash"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import CartService from "../cart"
|
||||
import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory"
|
||||
import { IsNull, Not } from "typeorm"
|
||||
import { PaymentSessionStatus } from "../../models"
|
||||
import TaxCalculationStrategy from "../../strategies/tax-calculation"
|
||||
import { cacheServiceMock } from "../__mocks__/cache"
|
||||
import { CustomerServiceMock } from "../__mocks__/customer"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||
import { LineItemAdjustmentServiceMock } from "../__mocks__/line-item-adjustment"
|
||||
import { newTotalsServiceMock } from "../__mocks__/new-totals"
|
||||
import { taxProviderServiceMock } from "../__mocks__/tax-provider"
|
||||
import { PaymentSessionStatus } from "../../models"
|
||||
import { NewTotalsService, TaxProviderService } from "../index"
|
||||
import { cacheServiceMock } from "../__mocks__/cache"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
||||
import { ProductServiceMock } from "../__mocks__/product"
|
||||
import { ProductVariantServiceMock } from "../__mocks__/product-variant"
|
||||
import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory"
|
||||
import { RegionServiceMock } from "../__mocks__/region"
|
||||
import { LineItemServiceMock } from "../__mocks__/line-item"
|
||||
import { ShippingOptionServiceMock } from "../__mocks__/shipping-option"
|
||||
import { CustomerServiceMock } from "../__mocks__/customer"
|
||||
import TaxCalculationStrategy from "../../strategies/tax-calculation"
|
||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||
import { taxProviderServiceMock } from "../__mocks__/tax-provider"
|
||||
import CartService from "../cart"
|
||||
import { NewTotalsService, TaxProviderService } from "../index"
|
||||
import SystemTaxService from "../system-tax"
|
||||
import { IsNull, Not } from "typeorm"
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn(),
|
||||
@@ -2628,6 +2629,7 @@ describe("CartService", () => {
|
||||
.register("regionService", asValue(RegionServiceMock))
|
||||
.register("lineItemService", asValue(LineItemServiceMock))
|
||||
.register("shippingOptionService", asValue(ShippingOptionServiceMock))
|
||||
.register("shippingProfileService", asValue(ShippingProfileServiceMock))
|
||||
.register("customerService", asValue(CustomerServiceMock))
|
||||
.register("discountService", asValue({}))
|
||||
.register("giftCardService", asValue({}))
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
import ShippingProfileService from "../shipping-profile"
|
||||
|
||||
@@ -15,6 +16,7 @@ describe("ShippingProfileService", () => {
|
||||
const profileService = new ShippingProfileService({
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
await profileService.retrieve(IdMap.getId("validId"))
|
||||
@@ -53,6 +55,7 @@ describe("ShippingProfileService", () => {
|
||||
shippingProfileRepository: profRepo,
|
||||
productService,
|
||||
shippingOptionService,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -106,6 +109,7 @@ describe("ShippingProfileService", () => {
|
||||
const profileService = new ShippingProfileService({
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -136,6 +140,7 @@ describe("ShippingProfileService", () => {
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
productService,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -219,6 +224,7 @@ describe("ShippingProfileService", () => {
|
||||
shippingProfileRepository: profRepo,
|
||||
shippingOptionService,
|
||||
customShippingOptionService,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -311,6 +317,7 @@ describe("ShippingProfileService", () => {
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
shippingOptionService,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -335,6 +342,7 @@ describe("ShippingProfileService", () => {
|
||||
const profileService = new ShippingProfileService({
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
featureFlagRouter: new FlagRouter({}),
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -18,11 +18,13 @@ import {
|
||||
RegionService,
|
||||
SalesChannelService,
|
||||
ShippingOptionService,
|
||||
ShippingProfileService,
|
||||
StoreService,
|
||||
TaxProviderService,
|
||||
TotalsService,
|
||||
} from "."
|
||||
import { IPriceSelectionStrategy, TransactionBaseService } from "../interfaces"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
|
||||
import {
|
||||
Address,
|
||||
@@ -79,6 +81,7 @@ type InjectedDependencies = {
|
||||
regionService: RegionService
|
||||
lineItemService: LineItemService
|
||||
shippingOptionService: ShippingOptionService
|
||||
shippingProfileService: ShippingProfileService
|
||||
customerService: CustomerService
|
||||
discountService: DiscountService
|
||||
giftCardService: GiftCardService
|
||||
@@ -119,6 +122,7 @@ class CartService extends TransactionBaseService {
|
||||
protected readonly paymentProviderService_: PaymentProviderService
|
||||
protected readonly customerService_: CustomerService
|
||||
protected readonly shippingOptionService_: ShippingOptionService
|
||||
protected readonly shippingProfileService_: ShippingProfileService
|
||||
protected readonly discountService_: DiscountService
|
||||
protected readonly giftCardService_: GiftCardService
|
||||
protected readonly taxProviderService_: TaxProviderService
|
||||
@@ -143,6 +147,7 @@ class CartService extends TransactionBaseService {
|
||||
regionService,
|
||||
lineItemService,
|
||||
shippingOptionService,
|
||||
shippingProfileService,
|
||||
customerService,
|
||||
discountService,
|
||||
giftCardService,
|
||||
@@ -172,6 +177,7 @@ class CartService extends TransactionBaseService {
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
this.customerService_ = customerService
|
||||
this.shippingOptionService_ = shippingOptionService
|
||||
this.shippingProfileService_ = shippingProfileService
|
||||
this.discountService_ = discountService
|
||||
this.giftCardService_ = giftCardService
|
||||
this.totalsService_ = totalsService
|
||||
@@ -222,6 +228,21 @@ class CartService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
IsolateProductDomainFeatureFlag.key
|
||||
)
|
||||
) {
|
||||
if (Array.isArray(options.relations)) {
|
||||
for (let i = 0; i < options.relations.length; i++) {
|
||||
if (options.relations[i].startsWith("items.variant")) {
|
||||
options.relations[i] = "items"
|
||||
}
|
||||
}
|
||||
}
|
||||
options.relations = [...new Set(options.relations)]
|
||||
}
|
||||
|
||||
const { totalsToSelect } = this.transformQueryForTotals_(options)
|
||||
|
||||
if (totalsToSelect.length) {
|
||||
@@ -293,10 +314,25 @@ class CartService extends TransactionBaseService {
|
||||
): Promise<WithRequiredProperty<Cart, "total">> {
|
||||
const relations = this.getTotalsRelations(options)
|
||||
|
||||
const cart = await this.retrieve(cartId, {
|
||||
...options,
|
||||
relations,
|
||||
})
|
||||
const opt = { ...options, relations }
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
IsolateProductDomainFeatureFlag.key
|
||||
)
|
||||
) {
|
||||
if (Array.isArray(opt.relations)) {
|
||||
for (let i = 0; i < opt.relations.length; i++) {
|
||||
if (opt.relations[i].startsWith("items.variant")) {
|
||||
opt.relations[i] = "items"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opt.relations = [...new Set(opt.relations)]
|
||||
}
|
||||
|
||||
const cart = await this.retrieve(cartId, opt)
|
||||
|
||||
return await this.decorateTotals(cart, totalsConfig)
|
||||
}
|
||||
@@ -547,23 +583,21 @@ class CartService extends TransactionBaseService {
|
||||
*/
|
||||
protected validateLineItemShipping_(
|
||||
shippingMethods: ShippingMethod[],
|
||||
lineItem: LineItem
|
||||
lineItemShippingProfiledId: string
|
||||
): boolean {
|
||||
if (!lineItem.variant_id) {
|
||||
if (!lineItemShippingProfiledId) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
shippingMethods &&
|
||||
shippingMethods.length &&
|
||||
lineItem.variant &&
|
||||
lineItem.variant.product
|
||||
lineItemShippingProfiledId
|
||||
) {
|
||||
const productProfile = lineItem.variant.product.profile_id
|
||||
const selectedProfiles = shippingMethods.map(
|
||||
({ shipping_option }) => shipping_option.profile_id
|
||||
)
|
||||
return selectedProfiles.includes(productProfile)
|
||||
return selectedProfiles.includes(lineItemShippingProfiledId)
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -1083,15 +1117,6 @@ class CartService extends TransactionBaseService {
|
||||
"discounts.rule",
|
||||
]
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
SalesChannelFeatureFlag.key
|
||||
) &&
|
||||
data.sales_channel_id
|
||||
) {
|
||||
relations.push("items.variant.product.profiles")
|
||||
}
|
||||
|
||||
const cart = await this.retrieve(cartId, {
|
||||
relations,
|
||||
})
|
||||
@@ -2163,10 +2188,33 @@ class CartService extends TransactionBaseService {
|
||||
const lineItemServiceTx =
|
||||
this.lineItemService_.withTransaction(transactionManager)
|
||||
|
||||
let productShippinProfileMap = new Map<string, string>()
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
IsolateProductDomainFeatureFlag.key
|
||||
)
|
||||
) {
|
||||
productShippinProfileMap =
|
||||
await this.shippingProfileService_.getMapProfileIdsByProductIds(
|
||||
cart.items.map((item) => item.variant.product_id)
|
||||
)
|
||||
} else {
|
||||
productShippinProfileMap = new Map<string, string>(
|
||||
cart.items.map((item) => [
|
||||
item.variant?.product?.id,
|
||||
item.variant?.product?.profile_id,
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
cart.items.map(async (item) => {
|
||||
return lineItemServiceTx.update(item.id, {
|
||||
has_shipping: this.validateLineItemShipping_(methods, item),
|
||||
has_shipping: this.validateLineItemShipping_(
|
||||
methods,
|
||||
productShippinProfileMap.get(item.variant?.product_id)!
|
||||
),
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
@@ -3,31 +3,31 @@ import { parse, toSeconds } from "iso8601-duration"
|
||||
import { isEmpty, omit } from "lodash"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import {
|
||||
DeepPartial,
|
||||
EntityManager,
|
||||
FindOptionsWhere,
|
||||
ILike,
|
||||
In,
|
||||
DeepPartial,
|
||||
EntityManager,
|
||||
FindOptionsWhere,
|
||||
ILike,
|
||||
In,
|
||||
} from "typeorm"
|
||||
import {
|
||||
NewTotalsService,
|
||||
ProductService,
|
||||
RegionService,
|
||||
TotalsService,
|
||||
NewTotalsService,
|
||||
ProductService,
|
||||
RegionService,
|
||||
TotalsService,
|
||||
} from "."
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import {
|
||||
Cart,
|
||||
Discount,
|
||||
DiscountConditionType,
|
||||
LineItem,
|
||||
Region,
|
||||
Cart,
|
||||
Discount,
|
||||
DiscountConditionType,
|
||||
LineItem,
|
||||
Region,
|
||||
} from "../models"
|
||||
import {
|
||||
AllocationType as DiscountAllocation,
|
||||
DiscountRule,
|
||||
DiscountRuleType,
|
||||
AllocationType as DiscountAllocation,
|
||||
DiscountRule,
|
||||
DiscountRuleType,
|
||||
} from "../models/discount-rule"
|
||||
import { DiscountRepository } from "../repositories/discount"
|
||||
import { DiscountConditionRepository } from "../repositories/discount-condition"
|
||||
@@ -35,12 +35,12 @@ import { DiscountRuleRepository } from "../repositories/discount-rule"
|
||||
import { GiftCardRepository } from "../repositories/gift-card"
|
||||
import { FindConfig, Selector } from "../types/common"
|
||||
import {
|
||||
CreateDiscountInput,
|
||||
CreateDiscountRuleInput,
|
||||
CreateDynamicDiscountInput,
|
||||
FilterableDiscountProps,
|
||||
UpdateDiscountInput,
|
||||
UpdateDiscountRuleInput,
|
||||
CreateDiscountInput,
|
||||
CreateDiscountRuleInput,
|
||||
CreateDynamicDiscountInput,
|
||||
FilterableDiscountProps,
|
||||
UpdateDiscountInput,
|
||||
UpdateDiscountRuleInput,
|
||||
} from "../types/discount"
|
||||
import { CalculationContextData } from "../types/totals"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
@@ -576,7 +576,7 @@ class DiscountService extends TransactionBaseService {
|
||||
|
||||
async validateDiscountForProduct(
|
||||
discountRuleId: string,
|
||||
productId: string | undefined
|
||||
productId?: string
|
||||
): Promise<boolean> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const discountConditionRepo = manager.withRepository(
|
||||
@@ -589,15 +589,9 @@ class DiscountService extends TransactionBaseService {
|
||||
return false
|
||||
}
|
||||
|
||||
const product = await this.productService_
|
||||
.withTransaction(manager)
|
||||
.retrieve(productId, {
|
||||
relations: ["tags"],
|
||||
})
|
||||
|
||||
return await discountConditionRepo.isValidForProduct(
|
||||
discountRuleId,
|
||||
product.id
|
||||
productId
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager, FindOperator, In } from "typeorm"
|
||||
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { Cart, DiscountRuleType, LineItem, LineItemAdjustment } from "../models"
|
||||
import { LineItemAdjustmentRepository } from "../repositories/line-item-adjustment"
|
||||
import { FindConfig } from "../types/common"
|
||||
import { FilterableLineItemAdjustmentProps } from "../types/line-item-adjustment"
|
||||
import DiscountService from "./discount"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import { CalculationContextData } from "../types/totals"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import DiscountService from "./discount"
|
||||
|
||||
type LineItemAdjustmentServiceProps = {
|
||||
manager: EntityManager
|
||||
|
||||
@@ -4,12 +4,13 @@ import { DeepPartial } from "typeorm/common/DeepPartial"
|
||||
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import {
|
||||
LineItem,
|
||||
LineItemAdjustment,
|
||||
LineItemTaxLine,
|
||||
ProductVariant,
|
||||
LineItem,
|
||||
LineItemAdjustment,
|
||||
LineItemTaxLine,
|
||||
ProductVariant,
|
||||
} from "../models"
|
||||
import { CartRepository } from "../repositories/cart"
|
||||
import { LineItemRepository } from "../repositories/line-item"
|
||||
@@ -19,11 +20,11 @@ import { GenerateInputData, GenerateLineItemContext } from "../types/line-item"
|
||||
import { ProductVariantPricing } from "../types/pricing"
|
||||
import { buildQuery, isString, setMetadata } from "../utils"
|
||||
import {
|
||||
PricingService,
|
||||
ProductService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
TaxProviderService,
|
||||
PricingService,
|
||||
ProductService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
TaxProviderService,
|
||||
} from "./index"
|
||||
import LineItemAdjustmentService from "./line-item-adjustment"
|
||||
|
||||
@@ -364,6 +365,14 @@ class LineItemService extends TransactionBaseService {
|
||||
should_merge: shouldMerge,
|
||||
}
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
IsolateProductDomainFeatureFlag.key
|
||||
)
|
||||
) {
|
||||
rawLineItem.product_id = variant.product_id
|
||||
}
|
||||
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
TaxInclusivePricingFeatureFlag.key
|
||||
@@ -471,7 +480,9 @@ class LineItemService extends TransactionBaseService {
|
||||
|
||||
return await lineItemRepository
|
||||
.findOne({ where: { id } })
|
||||
.then((lineItem) => lineItem && lineItemRepository.remove(lineItem))
|
||||
.then(
|
||||
async (lineItem) => lineItem && lineItemRepository.remove(lineItem)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@ import { FlagRouter } from "@medusajs/utils"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
ITaxCalculationStrategy,
|
||||
TaxCalculationContext,
|
||||
TransactionBaseService,
|
||||
ITaxCalculationStrategy,
|
||||
TaxCalculationContext,
|
||||
TransactionBaseService,
|
||||
} from "../interfaces"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import {
|
||||
Discount,
|
||||
DiscountRuleType,
|
||||
GiftCard,
|
||||
LineItem,
|
||||
LineItemTaxLine,
|
||||
Region,
|
||||
ShippingMethod,
|
||||
ShippingMethodTaxLine,
|
||||
Discount,
|
||||
DiscountRuleType,
|
||||
GiftCard,
|
||||
LineItem,
|
||||
LineItemTaxLine,
|
||||
Region,
|
||||
ShippingMethod,
|
||||
ShippingMethodTaxLine,
|
||||
} from "../models"
|
||||
import { LineAllocationsMap } from "../types/totals"
|
||||
import { calculatePriceTaxAmount } from "../utils"
|
||||
@@ -675,7 +675,7 @@ export default class NewTotalsService extends TransactionBaseService {
|
||||
if (!totals.tax_lines) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.UNEXPECTED_STATE,
|
||||
"Tax Lines must be joined to calculate taxes"
|
||||
"Tax Lines must be joined to calculate shipping taxes"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { FlagRouter, isDefined } from "@medusajs/utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager, In } from "typeorm"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import IsolateProductDomainFeatureFlag from "../loaders/feature-flags/isolate-product-domain"
|
||||
import {
|
||||
Cart,
|
||||
CustomShippingOption,
|
||||
@@ -27,6 +29,7 @@ type InjectedDependencies = {
|
||||
customShippingOptionService: CustomShippingOptionService
|
||||
shippingProfileRepository: typeof ShippingProfileRepository
|
||||
productRepository: typeof ProductRepository
|
||||
featureFlagRouter: FlagRouter
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,6 +44,7 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
// eslint-disable-next-line max-len
|
||||
protected readonly shippingProfileRepository_: typeof ShippingProfileRepository
|
||||
protected readonly productRepository_: typeof ProductRepository
|
||||
protected readonly featureFlagRouter_: FlagRouter
|
||||
|
||||
constructor({
|
||||
shippingProfileRepository,
|
||||
@@ -48,6 +52,7 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
productRepository,
|
||||
shippingOptionService,
|
||||
customShippingOptionService,
|
||||
featureFlagRouter,
|
||||
}: InjectedDependencies) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
@@ -57,6 +62,7 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
this.productRepository_ = productRepository
|
||||
this.shippingOptionService_ = shippingOptionService
|
||||
this.customShippingOptionService_ = customShippingOptionService
|
||||
this.featureFlagRouter_ = featureFlagRouter
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,49 +85,39 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
return shippingProfileRepo.find(query)
|
||||
}
|
||||
|
||||
async fetchOptionsByProductIds(
|
||||
productIds: string[],
|
||||
filter: Selector<ShippingOption>
|
||||
): Promise<ShippingOption[]> {
|
||||
const products = await this.productService_.list(
|
||||
{
|
||||
id: productIds,
|
||||
async getMapProfileIdsByProductIds(
|
||||
productIds: string[]
|
||||
): Promise<Map<string, string>> {
|
||||
const mappedProfiles = new Map<string, string>()
|
||||
|
||||
if (!productIds?.length) {
|
||||
return mappedProfiles
|
||||
}
|
||||
|
||||
const shippingProfiles = await this.shippingProfileRepository_.find({
|
||||
select: {
|
||||
id: true,
|
||||
products: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
relations: [
|
||||
"profile",
|
||||
"profile.shipping_options",
|
||||
"profile.shipping_options.requirements",
|
||||
],
|
||||
}
|
||||
)
|
||||
where: {
|
||||
products: {
|
||||
id: In(productIds),
|
||||
},
|
||||
},
|
||||
relations: {
|
||||
products: true,
|
||||
},
|
||||
})
|
||||
|
||||
const profiles = products.map((p) => p.profile)
|
||||
|
||||
const shippingOptions = profiles.reduce(
|
||||
(acc: ShippingOption[], next: ShippingProfile) =>
|
||||
acc.concat(next.shipping_options),
|
||||
[]
|
||||
)
|
||||
|
||||
const options = await Promise.all(
|
||||
shippingOptions.map(async (option) => {
|
||||
let canSend = true
|
||||
if (filter.region_id) {
|
||||
if (filter.region_id !== option.region_id) {
|
||||
canSend = false
|
||||
}
|
||||
}
|
||||
|
||||
if (option.deleted_at !== null) {
|
||||
canSend = false
|
||||
}
|
||||
|
||||
return canSend ? option : null
|
||||
shippingProfiles.forEach((profile) => {
|
||||
profile.products.forEach((product) => {
|
||||
mappedProfiles.set(product.id, profile.id)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
return options.filter(Boolean) as ShippingOption[]
|
||||
return mappedProfiles
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,7 +421,7 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
*/
|
||||
async fetchCartOptions(cart): Promise<ShippingOption[]> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
const profileIds = this.getProfilesInCart(cart)
|
||||
const profileIds = await this.getProfilesInCart(cart)
|
||||
|
||||
const selector: Selector<ShippingOption> = {
|
||||
profile_id: profileIds,
|
||||
@@ -489,14 +485,25 @@ class ShippingProfileService extends TransactionBaseService {
|
||||
* @param cart - the cart to extract products from
|
||||
* @return a list of product ids
|
||||
*/
|
||||
protected getProfilesInCart(cart: Cart): string[] {
|
||||
const profileIds = new Set<string>()
|
||||
protected async getProfilesInCart(cart: Cart): Promise<string[]> {
|
||||
let profileIds = new Set<string>()
|
||||
|
||||
cart.items.forEach((item) => {
|
||||
if (item.variant?.product) {
|
||||
profileIds.add(item.variant.product.profile_id)
|
||||
}
|
||||
})
|
||||
if (
|
||||
this.featureFlagRouter_.isFeatureEnabled(
|
||||
IsolateProductDomainFeatureFlag.key
|
||||
)
|
||||
) {
|
||||
const productShippinProfileMap = await this.getMapProfileIdsByProductIds(
|
||||
cart.items.map((item) => item.variant?.product_id)
|
||||
)
|
||||
profileIds = new Set([...productShippinProfileMap.values()])
|
||||
} else {
|
||||
cart.items.forEach((item) => {
|
||||
if (item.variant?.product) {
|
||||
profileIds.add(item.variant.product.profile_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return [...profileIds]
|
||||
}
|
||||
|
||||
@@ -247,46 +247,39 @@ class TaxProviderService extends TransactionBaseService {
|
||||
lineItems: LineItem[],
|
||||
calculationContext: TaxCalculationContext
|
||||
): Promise<(ShippingMethodTaxLine | LineItemTaxLine)[]> {
|
||||
const productIds = lineItems
|
||||
.map((l) => l?.variant?.product_id)
|
||||
.filter((p) => p)
|
||||
const productIds = [
|
||||
...new Set(
|
||||
lineItems.map((item) => item?.variant?.product_id).filter((p) => p)
|
||||
),
|
||||
]
|
||||
|
||||
const productRatesMap = await this.getRegionRatesForProduct(
|
||||
productIds,
|
||||
calculationContext.region
|
||||
)
|
||||
|
||||
const calculationLines = await Promise.all(
|
||||
lineItems.map(async (l) => {
|
||||
if (l.is_return) {
|
||||
return null
|
||||
}
|
||||
const calculationLines = lineItems.map((item) => {
|
||||
if (item.is_return) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (l.variant_id && !l.variant) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Unable to get the tax lines for the item ${l.id}, it contains a variant_id but the variant is missing.`
|
||||
)
|
||||
}
|
||||
|
||||
if (l.variant?.product_id) {
|
||||
return {
|
||||
item: l,
|
||||
rates: productRatesMap.get(l.variant.product_id) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the line item is custom and therefore not associated with a
|
||||
* product we assume no taxes - we should consider adding rate overrides
|
||||
* to custom lines at some point
|
||||
*/
|
||||
if (item.variant?.product_id) {
|
||||
return {
|
||||
item: l,
|
||||
rates: [],
|
||||
item: item,
|
||||
rates: productRatesMap.get(item.variant?.product_id) ?? [],
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* If the line item is custom and therefore not associated with a
|
||||
* product we assume no taxes - we should consider adding rate overrides
|
||||
* to custom lines at some point
|
||||
*/
|
||||
return {
|
||||
item: item,
|
||||
rates: [],
|
||||
}
|
||||
})
|
||||
|
||||
const shippingCalculationLines = await Promise.all(
|
||||
calculationContext.shipping_methods.map(async (sm) => {
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { isDefined, MedusaError } from "@medusajs/utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
ITaxCalculationStrategy,
|
||||
TaxCalculationContext,
|
||||
TransactionBaseService,
|
||||
ITaxCalculationStrategy,
|
||||
TaxCalculationContext,
|
||||
TransactionBaseService,
|
||||
} from "../interfaces"
|
||||
import {
|
||||
Cart,
|
||||
ClaimOrder,
|
||||
Discount,
|
||||
DiscountRuleType,
|
||||
LineItem,
|
||||
LineItemTaxLine,
|
||||
Order,
|
||||
ShippingMethod,
|
||||
ShippingMethodTaxLine,
|
||||
Swap,
|
||||
Cart,
|
||||
ClaimOrder,
|
||||
Discount,
|
||||
DiscountRuleType,
|
||||
LineItem,
|
||||
LineItemTaxLine,
|
||||
Order,
|
||||
ShippingMethod,
|
||||
ShippingMethodTaxLine,
|
||||
Swap,
|
||||
} from "../models"
|
||||
import { isCart } from "../types/cart"
|
||||
import { isOrder } from "../types/orders"
|
||||
import {
|
||||
CalculationContextData,
|
||||
LineAllocationsMap,
|
||||
LineDiscount,
|
||||
LineDiscountAmount,
|
||||
SubtotalOptions,
|
||||
CalculationContextData,
|
||||
LineAllocationsMap,
|
||||
LineDiscount,
|
||||
LineDiscountAmount,
|
||||
SubtotalOptions,
|
||||
} from "../types/totals"
|
||||
import { NewTotalsService, TaxProviderService } from "./index"
|
||||
|
||||
@@ -622,6 +622,7 @@ class TotalsService extends TransactionBaseService {
|
||||
* @param value - discount value
|
||||
* @param discountType - the type of discount (fixed or percentage)
|
||||
* @return triples of lineitem, variant and applied discount
|
||||
* @deprecated - in favour of DiscountService.calculateDiscountForLineItem
|
||||
*/
|
||||
calculateDiscount_(
|
||||
lineItem: LineItem,
|
||||
|
||||
@@ -557,14 +557,15 @@ export class RemoteJoiner {
|
||||
parsedExpands: Map<string, RemoteExpandProperty>
|
||||
): Map<string, RemoteExpandProperty> {
|
||||
const mergedExpands = new Map<string, RemoteExpandProperty>(parsedExpands)
|
||||
const mergedPaths = new Map<string, string>()
|
||||
const mergedPaths = new Map<string, RemoteExpandProperty>()
|
||||
|
||||
for (const [path, expand] of mergedExpands.entries()) {
|
||||
const currentServiceName = expand.serviceConfig.serviceName
|
||||
let parentPath = expand.parent
|
||||
|
||||
while (parentPath) {
|
||||
const parentExpand = mergedExpands.get(parentPath)
|
||||
const parentExpand =
|
||||
mergedExpands.get(parentPath) ?? mergedPaths.get(parentPath)
|
||||
if (
|
||||
!parentExpand ||
|
||||
parentExpand.serviceConfig.serviceName !== currentServiceName
|
||||
@@ -588,7 +589,7 @@ export class RemoteJoiner {
|
||||
targetExpand.args = expand.args
|
||||
|
||||
mergedExpands.delete(path)
|
||||
mergedPaths.set(path, parentPath)
|
||||
mergedPaths.set(path, expand)
|
||||
|
||||
parentPath = parentExpand.parent
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"outputs": [
|
||||
"!node_modules/**",
|
||||
"!src/**",
|
||||
"/*/**"
|
||||
"*/**"
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
|
||||
Reference in New Issue
Block a user