feat(medusa): Separate money amount and variant (#4906)
* initial changes * working test * final changes to product tests * update integration tests * update price list integration tests * update integration tests * update unit tests * update plugin integration tests * remove catch from integration test * undo change * add andWhere * update upsertCurrencyMoneyAmount method * undo line item changes * undo changes * update deprecated method * Update packages/medusa/src/migrations/1692953518123-drop_money_amount_constraints_for_pricing_module.ts Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com> * rename joinTable * update with joinTable entity * update load methods * remove await create * re-add context test * update price list behavior for prices * update price list snapshots * re-add admin seeder * pr feedback * fix unit tests * fix plugin integration tests * initial review changes * redo changes to variant creation --------- Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
52 files changed
+2465
-910
No files matched your search
@@ -1,20 +1,22 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { PriceList, Product } from "../../../.."
|
||||
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
||||
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares, {
|
||||
transformBody,
|
||||
transformQuery,
|
||||
} from "../../../middlewares"
|
||||
import { PriceList, Product } from "../../../.."
|
||||
import {
|
||||
defaultAdminProductFields,
|
||||
defaultAdminProductRelations,
|
||||
} from "../products"
|
||||
import { AdminPostPriceListsPriceListReq } from "./create-price-list"
|
||||
import { AdminGetPriceListsPriceListProductsParams } from "./list-price-list-products"
|
||||
import middlewares, {
|
||||
transformBody,
|
||||
transformQuery,
|
||||
} from "../../../middlewares"
|
||||
|
||||
import { AdminGetPriceListPaginationParams } from "./list-price-lists"
|
||||
import { AdminGetPriceListsPriceListProductsParams } from "./list-price-list-products"
|
||||
import { AdminPostPriceListsPriceListReq } from "./create-price-list"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { Router } from "express"
|
||||
import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -91,7 +93,11 @@ export const defaultAdminPriceListFields = [
|
||||
"deleted_at",
|
||||
]
|
||||
|
||||
export const defaultAdminPriceListRelations = ["prices", "customer_groups"]
|
||||
export const defaultAdminPriceListRelations = [
|
||||
"prices",
|
||||
"prices.variants",
|
||||
"customer_groups",
|
||||
]
|
||||
|
||||
/**
|
||||
* @schema AdminPriceListRes
|
||||
@@ -285,4 +291,3 @@ export * from "./get-price-list"
|
||||
export * from "./list-price-list-products"
|
||||
export * from "./list-price-lists"
|
||||
export * from "./update-price-list"
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
@@ -17,7 +18,6 @@ import { ProductStatus } from "../../../../models"
|
||||
import { Request } from "express"
|
||||
import { Type } from "class-transformer"
|
||||
import { pickBy } from "lodash"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/price-lists/{id}/products
|
||||
|
||||
@@ -407,4 +407,3 @@ export * from "./set-metadata"
|
||||
export * from "./update-option"
|
||||
export * from "./update-product"
|
||||
export * from "./update-variant"
|
||||
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
SalesChannelService,
|
||||
} from "../../../../services"
|
||||
|
||||
import { IInventoryService } from "@medusajs/types"
|
||||
import { Type } from "class-transformer"
|
||||
import { Product } from "../../../../models"
|
||||
import { PricedProduct } from "../../../../types/pricing"
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
import { IInventoryService } from "@medusajs/types"
|
||||
import { PricedProduct } from "../../../../types/pricing"
|
||||
import { Product } from "../../../../models"
|
||||
import { Type } from "class-transformer"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/products
|
||||
|
||||
+2
-2
@@ -5,14 +5,14 @@ export class lineItemTaxAdjustmentOnCascadeDelete1680857773272
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "line_item_tax_line" DROP CONSTRAINT "FK_5077fa54b0d037e984385dfe8ad"`
|
||||
`ALTER TABLE "line_item_tax_line" DROP CONSTRAINT IF EXISTS "FK_5077fa54b0d037e984385dfe8ad"`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "line_item_tax_line" ADD CONSTRAINT "FK_5077fa54b0d037e984385dfe8ad" FOREIGN KEY ("item_id") REFERENCES "line_item"("id") ON DELETE CASCADE ON UPDATE NO ACTION`
|
||||
)
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "line_item_adjustment" DROP CONSTRAINT "FK_be9aea2ccf3567007b6227da4d2"`
|
||||
`ALTER TABLE "line_item_adjustment" DROP CONSTRAINT IF EXISTS "FK_be9aea2ccf3567007b6227da4d2"`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "line_item_adjustment" ADD CONSTRAINT "FK_be9aea2ccf3567007b6227da4d2" FOREIGN KEY ("item_id") REFERENCES "line_item"("id") ON DELETE CASCADE ON UPDATE NO ACTION`
|
||||
|
||||
@@ -32,14 +32,14 @@ export class addTableProductShippingProfile1680857773273
|
||||
DROP INDEX IF EXISTS "idx_product_shipping_profile_product_id";
|
||||
DROP INDEX IF EXISTS "idx_product_shipping_profile_profile_id";
|
||||
|
||||
DROP TABLE IF EXISTS "product_shipping_profile";
|
||||
|
||||
ALTER TABLE "product" ADD COLUMN IF NOT EXISTS "profile_id";
|
||||
|
||||
UPDATE "product" SET "profile_id" = "product_shipping_profile"."profile_id"
|
||||
FROM "product_shipping_profile"
|
||||
WHERE "product"."id" = "product_shipping_profile"."product_id";
|
||||
|
||||
DROP TABLE IF EXISTS "product_shipping_profile";
|
||||
|
||||
ALTER TABLE "product" ALTER COLUMN profile_id SET NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_80823b7ae866dc5acae2dac6d2" ON "product" ("profile_id");
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||
|
||||
export class dropMoneyAmountConstraintsForPricingModule1692953518123
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = "dropMoneyAmountConstraintsForPricingModule1692953518123"
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS "product_variant_money_amount"
|
||||
(
|
||||
"id" character varying NOT NULL,
|
||||
"money_amount_id" text NOT NULL,
|
||||
"variant_id" text NOT NULL,
|
||||
"deleted_at" TIMESTAMP WITH TIME ZONE,
|
||||
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_product_variant_money_amount" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
INSERT INTO "product_variant_money_amount"("id", "money_amount_id", "variant_id")
|
||||
SELECT CONCAT('pvma_', id), "id", "variant_id" FROM "money_amount";
|
||||
|
||||
ALTER TABLE "money_amount" DROP COLUMN IF EXISTS "variant_id";
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "idx_product_variant_money_amount_money_amount_id_unique" ON "product_variant_money_amount" ("money_amount_id");
|
||||
CREATE INDEX IF NOT EXISTS "idx_product_variant_money_amount_variant_id" ON "product_variant_money_amount" ("variant_id");
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`
|
||||
DROP INDEX IF EXISTS "idx_product_variant_money_amount_money_amount_id_unique";
|
||||
DROP INDEX IF EXISTS "idx_product_variant_money_amount_variant_id";
|
||||
|
||||
ALTER TABLE "product_variant_money_amount" ADD COLUMN IF NOT EXISTS "variant_id";
|
||||
|
||||
UPDATE "money_amount" SET "variant_id" = "product_variant_money_amount"."variant_id"
|
||||
FROM "product_variant_money_amount"
|
||||
WHERE "money_amount"."id" = "product_variant_money_amount"."money_amount_id";
|
||||
|
||||
DROP TABLE IF EXISTS "product_variant_money_amount";
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_product_variant_money_amount_id ON money_amount (variant_id);
|
||||
`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ export * from "./product-type"
|
||||
export * from "./product-type-tax-rate"
|
||||
export * from "./product-variant"
|
||||
export * from "./product-variant-inventory-item"
|
||||
export * from "./product-variant-money-amount"
|
||||
export * from "./publishable-api-key"
|
||||
export * from "./publishable-api-key-sales-channel"
|
||||
export * from "./refund"
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import {
|
||||
AfterLoad,
|
||||
BeforeInsert,
|
||||
BeforeUpdate,
|
||||
Column,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
} from "typeorm"
|
||||
|
||||
@@ -43,17 +47,27 @@ export class MoneyAmount extends SoftDeletableEntity {
|
||||
@JoinColumn({ name: "price_list_id" })
|
||||
price_list: PriceList | null
|
||||
|
||||
@Index('idx_money_amount_variant_id')
|
||||
@Column({ nullable: true })
|
||||
variant_id: string
|
||||
|
||||
@ManyToOne(() => ProductVariant, (variant) => variant.prices, {
|
||||
@ManyToMany(() => ProductVariant, {
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
@JoinColumn({ name: "variant_id" })
|
||||
@JoinTable({
|
||||
name: "product_variant_money_amount",
|
||||
joinColumn: {
|
||||
name: "money_amount_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "variant_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
variants: ProductVariant[]
|
||||
|
||||
variant: ProductVariant
|
||||
|
||||
@Index('idx_money_amount_region_id')
|
||||
variant_id: string
|
||||
|
||||
@Index("idx_money_amount_region_id")
|
||||
@Column({ nullable: true })
|
||||
region_id: string
|
||||
|
||||
@@ -64,6 +78,27 @@ export class MoneyAmount extends SoftDeletableEntity {
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): undefined | void {
|
||||
this.id = generateEntityId(this.id, "ma")
|
||||
|
||||
if (this.variant || this.variant_id) {
|
||||
this.variants = [
|
||||
{ id: this.variant?.id || this.variant_id },
|
||||
] as ProductVariant[]
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeUpdate()
|
||||
private beforeUpdate(): void {
|
||||
if (this.variant || this.variant_id) {
|
||||
this.variants = [
|
||||
{ id: this.variant?.id || this.variant_id },
|
||||
] as ProductVariant[]
|
||||
}
|
||||
}
|
||||
|
||||
@AfterLoad()
|
||||
private afterLoad() {
|
||||
this.variant = this.variants?.[0]
|
||||
this.variant_id = this.variant?.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export class ProductCategory extends BaseEntity {
|
||||
@Column()
|
||||
name: string
|
||||
|
||||
@Column({ nullable: false, default: '' })
|
||||
@Column({ nullable: false, default: "" })
|
||||
description: string
|
||||
|
||||
@Index({ unique: true })
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { BeforeInsert, Column, Entity, Index } from "typeorm"
|
||||
import { generateEntityId } from "../utils"
|
||||
import { SoftDeletableEntity } from "../interfaces"
|
||||
|
||||
@Entity()
|
||||
export class ProductVariantMoneyAmount extends SoftDeletableEntity {
|
||||
@Index("idx_product_variant_money_amount_money_amount_id_unique", {
|
||||
unique: true,
|
||||
})
|
||||
@Column()
|
||||
money_amount_id: string
|
||||
|
||||
@Index("idx_product_variant_money_amount_variant_id")
|
||||
@Column()
|
||||
variant_id: string
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert(): void {
|
||||
this.id = generateEntityId(this.id, "pvma_")
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
} from "typeorm"
|
||||
@@ -28,9 +30,19 @@ export class ProductVariant extends SoftDeletableEntity {
|
||||
@JoinColumn({ name: "product_id" })
|
||||
product: Product
|
||||
|
||||
@OneToMany(() => MoneyAmount, (ma) => ma.variant, {
|
||||
cascade: true,
|
||||
onDelete: "CASCADE",
|
||||
@ManyToMany(() => MoneyAmount, {
|
||||
cascade: ["remove", "soft-remove", "recover"],
|
||||
})
|
||||
@JoinTable({
|
||||
name: "product_variant_money_amount",
|
||||
joinColumn: {
|
||||
name: "variant_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
inverseJoinColumn: {
|
||||
name: "money_amount_id",
|
||||
referencedColumnName: "id",
|
||||
},
|
||||
})
|
||||
prices: MoneyAmount[]
|
||||
|
||||
|
||||
@@ -6,18 +6,24 @@ import {
|
||||
ObjectLiteral,
|
||||
WhereExpressionBuilder,
|
||||
} from "typeorm"
|
||||
import {
|
||||
MoneyAmount,
|
||||
ProductVariant,
|
||||
ProductVariantMoneyAmount,
|
||||
} from "../models"
|
||||
import {
|
||||
PriceListPriceCreateInput,
|
||||
PriceListPriceUpdateInput,
|
||||
} from "../types/price-list"
|
||||
|
||||
import { MoneyAmount } from "../models"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { ProductVariantPrice } from "../types/product-variant"
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"
|
||||
import { dataSource } from "../loaders/database"
|
||||
import { groupBy } from "lodash"
|
||||
import { isString } from "../utils"
|
||||
import partition from "lodash/partition"
|
||||
import { ulid } from "ulid"
|
||||
|
||||
type Price = Partial<
|
||||
Omit<MoneyAmount, "created_at" | "updated_at" | "deleted_at">
|
||||
@@ -36,15 +42,37 @@ export const MoneyAmountRepository = dataSource
|
||||
.into(MoneyAmount)
|
||||
.values(data)
|
||||
|
||||
let rawMoneyAmounts
|
||||
if (!queryBuilder.connection.driver.isReturningSqlSupported("insert")) {
|
||||
const rawMoneyAmounts = await queryBuilder.execute()
|
||||
return rawMoneyAmounts.generatedMaps.map((d) =>
|
||||
this.create(d)
|
||||
) as MoneyAmount[]
|
||||
rawMoneyAmounts = await queryBuilder.execute()
|
||||
} else {
|
||||
rawMoneyAmounts = await queryBuilder.returning("*").execute()
|
||||
}
|
||||
|
||||
const rawMoneyAmounts = await queryBuilder.returning("*").execute()
|
||||
return rawMoneyAmounts.generatedMaps.map((d) => this.create(d))
|
||||
const created = rawMoneyAmounts.generatedMaps.map((d) =>
|
||||
this.create(d)
|
||||
) as MoneyAmount[]
|
||||
|
||||
const variantMoneyAmounts = this.manager.create(
|
||||
ProductVariantMoneyAmount,
|
||||
data
|
||||
.filter(
|
||||
(
|
||||
d
|
||||
): d is {
|
||||
variant: QueryDeepPartialEntity<ProductVariant>
|
||||
id: string
|
||||
} => !!d.variant
|
||||
)
|
||||
.map((d) => ({
|
||||
variant_id: d.variant.id,
|
||||
money_amount_id: d.id,
|
||||
}))
|
||||
)
|
||||
|
||||
await this.manager.save(variantMoneyAmounts)
|
||||
|
||||
return created
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -57,8 +85,15 @@ export const MoneyAmountRepository = dataSource
|
||||
prices: Price[]
|
||||
): Promise<MoneyAmount[]> {
|
||||
const pricesNotInPricesPayload = await this.createQueryBuilder()
|
||||
.where({
|
||||
.leftJoinAndSelect(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.where("pvma.variant_id = :variant_id", {
|
||||
variant_id: variantId,
|
||||
})
|
||||
.andWhere({
|
||||
price_list_id: IsNull(),
|
||||
})
|
||||
.andWhere(
|
||||
@@ -87,11 +122,27 @@ export const MoneyAmountRepository = dataSource
|
||||
]
|
||||
: variantIdOrData
|
||||
|
||||
const queryBuilder = this.createQueryBuilder().delete()
|
||||
const maDeleteQueryBuilder = this.createQueryBuilder("ma")
|
||||
const mavDeleteQueryBuilder = this.createQueryBuilder()
|
||||
.delete()
|
||||
.from("product_variant_money_amount")
|
||||
|
||||
for (const data_ of data) {
|
||||
const maIdsForVariant = await this.createQueryBuilder("ma")
|
||||
.leftJoin(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.addSelect("pvma.variant_id", "variant_id")
|
||||
.addSelect("pvma.money_amount_id", "money_amount_id")
|
||||
.where("pvma.variant_id = :variant_id", {
|
||||
variant_id: data_.variantId,
|
||||
})
|
||||
.getMany()
|
||||
|
||||
const where = {
|
||||
variant_id: data_.variantId,
|
||||
id: In(maIdsForVariant.map((ma) => ma.id)),
|
||||
price_list_id: IsNull(),
|
||||
}
|
||||
|
||||
@@ -119,40 +170,72 @@ export const MoneyAmountRepository = dataSource
|
||||
}
|
||||
}
|
||||
|
||||
queryBuilder.orWhere(
|
||||
maDeleteQueryBuilder.orWhere(
|
||||
new Brackets((localQueryBuild) => {
|
||||
localQueryBuild.where(where).andWhere(orWhere)
|
||||
})
|
||||
)
|
||||
}
|
||||
const deleteAmounts = await maDeleteQueryBuilder.getMany()
|
||||
|
||||
await queryBuilder.execute()
|
||||
if (!deleteAmounts.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.delete(deleteAmounts.map((mav) => mav.id)),
|
||||
mavDeleteQueryBuilder
|
||||
.where(
|
||||
deleteAmounts.map((mav) => ({
|
||||
money_amount_id: mav.id,
|
||||
}))
|
||||
)
|
||||
.execute(),
|
||||
])
|
||||
},
|
||||
|
||||
async upsertVariantCurrencyPrice(
|
||||
variantId: string,
|
||||
price: Price
|
||||
): Promise<MoneyAmount> {
|
||||
let moneyAmount = await this.findOne({
|
||||
where: {
|
||||
currency_code: price.currency_code,
|
||||
variant_id: variantId,
|
||||
region_id: IsNull(),
|
||||
price_list_id: IsNull(),
|
||||
},
|
||||
})
|
||||
let moneyAmount = await this.createQueryBuilder()
|
||||
.leftJoinAndSelect(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.where("pvma.variant_id = :variantId", { variantId })
|
||||
.andWhere("ma.currency_code = :currencyCode", {
|
||||
currencyCode: price.currency_code,
|
||||
})
|
||||
.andWhere("ma.region_id IS NULL")
|
||||
.andWhere("ma.price_list_id IS NULL")
|
||||
.getOne()
|
||||
|
||||
const created = !moneyAmount
|
||||
|
||||
if (!moneyAmount) {
|
||||
moneyAmount = this.create({
|
||||
...price,
|
||||
currency_code: price.currency_code?.toLowerCase(),
|
||||
variant_id: variantId,
|
||||
variant: { id: variantId },
|
||||
})
|
||||
} else {
|
||||
moneyAmount.amount = price.amount
|
||||
}
|
||||
|
||||
return await this.save(moneyAmount)
|
||||
const createdAmount = await this.save(moneyAmount)
|
||||
|
||||
if (created) {
|
||||
await this.createProductVariantMoneyAmounts([
|
||||
{
|
||||
variant_id: variantId,
|
||||
money_amount_id: createdAmount.id,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
return createdAmount
|
||||
},
|
||||
|
||||
async addPriceListPrices(
|
||||
@@ -160,11 +243,29 @@ export const MoneyAmountRepository = dataSource
|
||||
prices: PriceListPriceCreateInput[],
|
||||
overrideExisting = false
|
||||
): Promise<MoneyAmount[]> {
|
||||
const toInsert = prices.map((price) =>
|
||||
this.create({
|
||||
...price,
|
||||
price_list_id: priceListId,
|
||||
})
|
||||
const [toInsert, joinTableValues] = prices.reduce(
|
||||
(acc, price) => {
|
||||
const [prices, joinTableValues] = acc
|
||||
const id = `ma_${ulid()}`
|
||||
const variant = this.create({
|
||||
...price,
|
||||
id,
|
||||
price_list_id: priceListId,
|
||||
})
|
||||
const joinTableValue = this.manager.create(
|
||||
ProductVariantMoneyAmount,
|
||||
{
|
||||
variant_id: price.variant_id!,
|
||||
money_amount_id: id,
|
||||
}
|
||||
)
|
||||
|
||||
return [
|
||||
[...prices, variant],
|
||||
[...joinTableValues, joinTableValue],
|
||||
]
|
||||
},
|
||||
[[], []] as [MoneyAmount[], ProductVariantMoneyAmount[]]
|
||||
)
|
||||
|
||||
const insertResult = await this.createQueryBuilder()
|
||||
@@ -172,19 +273,31 @@ export const MoneyAmountRepository = dataSource
|
||||
.orIgnore(true)
|
||||
.into(MoneyAmount)
|
||||
.values(toInsert as QueryDeepPartialEntity<MoneyAmount>[])
|
||||
.returning("*")
|
||||
.execute()
|
||||
|
||||
if (overrideExisting) {
|
||||
await this.createQueryBuilder()
|
||||
const { raw } = await this.createQueryBuilder()
|
||||
.delete()
|
||||
.from(MoneyAmount)
|
||||
.where({
|
||||
price_list_id: priceListId,
|
||||
id: Not(In(insertResult.identifiers.map((ma) => ma.id))),
|
||||
})
|
||||
.returning("id")
|
||||
.execute()
|
||||
|
||||
await this.createQueryBuilder()
|
||||
.delete()
|
||||
.from("product_variant_money_amount")
|
||||
.where({
|
||||
money_amount_id: In(raw.map((deletedMa) => deletedMa.id)),
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
await this.manager.save(joinTableValues)
|
||||
|
||||
return await this.manager
|
||||
.createQueryBuilder(MoneyAmount, "ma")
|
||||
.select()
|
||||
@@ -210,7 +323,12 @@ export const MoneyAmountRepository = dataSource
|
||||
): Promise<[MoneyAmount[], number]> {
|
||||
const qb = this.createQueryBuilder("ma")
|
||||
.leftJoinAndSelect("ma.price_list", "price_list")
|
||||
.where("ma.variant_id = :variant_id", { variant_id })
|
||||
.leftJoin(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.where("pvma.variant_id = :variant_id", { variant_id })
|
||||
|
||||
const getAndWhere = (subQb): WhereExpressionBuilder => {
|
||||
const andWhere = subQb.where("ma.price_list_id = :price_list_id", {
|
||||
@@ -256,6 +374,60 @@ export const MoneyAmountRepository = dataSource
|
||||
return [result[0][variant_id], result[1]]
|
||||
},
|
||||
|
||||
async findCurrencyMoneyAmounts(
|
||||
where: { variant_id: string; currency_code: string }[]
|
||||
) {
|
||||
const qb = this.createQueryBuilder("ma")
|
||||
.leftJoin(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.addSelect("pvma.variant_id", "variant_id")
|
||||
|
||||
where.forEach((variantCurrency, i) =>
|
||||
qb.orWhere(
|
||||
`(pvma.variant_id = :variant_id_${i} AND ma.currency_code = :currency_code_${i} AND ma.region_id IS NULL AND ma.price_list_id IS NULL)`,
|
||||
{
|
||||
[`variant_id_${i}`]: variantCurrency.variant_id,
|
||||
[`currency_code_${i}`]: variantCurrency.currency_code,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const rawAndEntities = await qb.getRawAndEntities()
|
||||
return rawAndEntities.entities.map((e, i) => {
|
||||
return { ...e, variant_id: rawAndEntities.raw[i].variant_id }
|
||||
})
|
||||
},
|
||||
|
||||
async findRegionMoneyAmounts(
|
||||
where: { variant_id: string; region_id: string }[]
|
||||
) {
|
||||
const qb = this.createQueryBuilder("ma")
|
||||
.leftJoin(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.addSelect("pvma.variant_id", "variant_id")
|
||||
|
||||
where.forEach((w, i) =>
|
||||
qb.orWhere(
|
||||
`(pvma.variant_id = :variant_id_${i} AND ma.region_id = :region_id_${i} AND ma.price_list_id IS NULL)`,
|
||||
{
|
||||
[`variant_id_${i}`]: w.variant_id,
|
||||
[`region_id_${i}`]: w.region_id,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const rawAndEntities = await qb.getRawAndEntities()
|
||||
return rawAndEntities.entities.map((e, i) => {
|
||||
return { ...e, variant_id: rawAndEntities.raw[i].variant_id }
|
||||
})
|
||||
},
|
||||
|
||||
async findManyForVariantsInRegion(
|
||||
variant_ids: string | string[],
|
||||
region_id?: string,
|
||||
@@ -266,11 +438,22 @@ export const MoneyAmountRepository = dataSource
|
||||
): Promise<[Record<string, MoneyAmount[]>, number]> {
|
||||
variant_ids = Array.isArray(variant_ids) ? variant_ids : [variant_ids]
|
||||
|
||||
if (!variant_ids.length) {
|
||||
return [{}, 0]
|
||||
}
|
||||
const date = new Date()
|
||||
|
||||
const qb = this.createQueryBuilder("ma")
|
||||
.leftJoinAndSelect("ma.price_list", "price_list")
|
||||
.where({ variant_id: In(variant_ids) })
|
||||
.leftJoinAndSelect(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.addSelect("pvma.variant_id", "variant_id")
|
||||
.andWhere("pvma.variant_id IN (:...variantIds)", {
|
||||
variantIds: variant_ids,
|
||||
})
|
||||
.andWhere("(ma.price_list_id is null or price_list.status = 'active')")
|
||||
.andWhere(
|
||||
"(price_list.ends_at is null OR price_list.ends_at > :date)",
|
||||
@@ -326,7 +509,16 @@ export const MoneyAmountRepository = dataSource
|
||||
)
|
||||
}
|
||||
|
||||
const [prices, count] = await qb.getManyAndCount()
|
||||
const count = await qb.getCount()
|
||||
const res = await qb.getRawAndEntities()
|
||||
|
||||
const { entities, raw } = res
|
||||
|
||||
const prices = entities.map((p, i) => {
|
||||
p["variant_id"] = raw[i]["variant_id"]
|
||||
return p
|
||||
})
|
||||
|
||||
const groupedPrices = groupBy(prices, "variant_id")
|
||||
|
||||
return [groupedPrices, count]
|
||||
@@ -341,11 +533,62 @@ export const MoneyAmountRepository = dataSource
|
||||
(update) => update.id
|
||||
)
|
||||
|
||||
const newPriceEntities = newPrices.map((price) =>
|
||||
this.create({ ...price, price_list_id: priceListId })
|
||||
const [newPriceEntities, joinTableValues] = newPrices.reduce(
|
||||
(acc, price) => {
|
||||
const [prices, joinTableValues] = acc
|
||||
const id = `ma_${ulid()}`
|
||||
const variantPrice = this.create({
|
||||
...price,
|
||||
id,
|
||||
price_list_id: priceListId,
|
||||
})
|
||||
const joinTableValue = this.manager.create(
|
||||
ProductVariantMoneyAmount,
|
||||
{
|
||||
variant_id: price.variant_id!,
|
||||
money_amount_id: id,
|
||||
}
|
||||
)
|
||||
|
||||
prices.push(variantPrice)
|
||||
joinTableValues.push(joinTableValue)
|
||||
|
||||
return [prices, joinTableValues]
|
||||
},
|
||||
[[], []] as [MoneyAmount[], ProductVariantMoneyAmount[]]
|
||||
)
|
||||
|
||||
return await this.save([...existingPrices, ...newPriceEntities])
|
||||
const [prices] = await Promise.all([
|
||||
this.save([...existingPrices, ...newPriceEntities]),
|
||||
await this.manager.save(joinTableValues),
|
||||
])
|
||||
|
||||
return prices
|
||||
},
|
||||
|
||||
async getPricesForVariantInRegion(
|
||||
variantId: string,
|
||||
regionId: string | undefined
|
||||
): Promise<MoneyAmount[]> {
|
||||
return this.createQueryBuilder()
|
||||
.leftJoinAndSelect(
|
||||
"product_variant_money_amount",
|
||||
"pvma",
|
||||
"pvma.money_amount_id = ma.id"
|
||||
)
|
||||
.where("pvma.variant_id = :variantId", { variantId })
|
||||
.where("ma.region_id = :regionId", { regionId })
|
||||
.getMany()
|
||||
},
|
||||
|
||||
async createProductVariantMoneyAmounts(
|
||||
toCreate: { variant_id: string; money_amount_id: string }[]
|
||||
) {
|
||||
return await this.createQueryBuilder()
|
||||
.insert()
|
||||
.into("product_variant_money_amount")
|
||||
.values(toCreate)
|
||||
.execute()
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -127,14 +127,19 @@ describe("PriceListService", () => {
|
||||
updateRelatedMoneyAmountRepository.create = jest
|
||||
.fn()
|
||||
.mockImplementation((rawEntity) => Promise.resolve(rawEntity))
|
||||
|
||||
updateRelatedMoneyAmountRepository.save = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve())
|
||||
updateRelatedMoneyAmountRepository.updatePriceListPrices =
|
||||
MoneyAmountRepository.updatePriceListPrices
|
||||
|
||||
const manager = {
|
||||
save: (entities) => Promise.resolve(entities),
|
||||
create: (entities) => Promise.resolve(entities),
|
||||
}
|
||||
const updateRelatedPriceListService = new PriceListService({
|
||||
manager: MockManager,
|
||||
manager: { ...MockManager, ...manager },
|
||||
customerGroupService,
|
||||
priceListRepository,
|
||||
moneyAmountRepository: updateRelatedMoneyAmountRepository,
|
||||
|
||||
@@ -670,6 +670,16 @@ describe("ProductVariantService", () => {
|
||||
insertBulk: (data) => data,
|
||||
})
|
||||
|
||||
const productVariantRepository = MockRepository({
|
||||
find: (query) => {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: IdMap.getId("ironman"),
|
||||
},
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
const oldPrices = [
|
||||
{
|
||||
currency_code: "dkk",
|
||||
@@ -686,6 +696,20 @@ describe("ProductVariantService", () => {
|
||||
moneyAmountRepository.upsertVariantCurrencyPrice = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve())
|
||||
moneyAmountRepository.findCurrencyMoneyAmounts = jest
|
||||
.fn()
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
id: IdMap.getId("dkk"),
|
||||
variant_id: IdMap.getId("ironman"),
|
||||
currency_code: "dkk",
|
||||
},
|
||||
])
|
||||
)
|
||||
moneyAmountRepository.findRegionMoneyAmounts = jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve([]))
|
||||
|
||||
const priceSelectionStrategy = {
|
||||
withTransaction: function () {
|
||||
@@ -723,6 +747,7 @@ describe("ProductVariantService", () => {
|
||||
regionService,
|
||||
priceSelectionStrategy,
|
||||
moneyAmountRepository,
|
||||
productVariantRepository,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -730,7 +755,7 @@ describe("ProductVariantService", () => {
|
||||
})
|
||||
|
||||
it("successfully removes obsolete prices and create new prices", async () => {
|
||||
await productVariantService.updateVariantPrices("ironman", [
|
||||
await productVariantService.updateVariantPrices(IdMap.getId("ironman"), [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 4000,
|
||||
@@ -744,7 +769,7 @@ describe("ProductVariantService", () => {
|
||||
expect(moneyAmountRepository.insertBulk).toHaveBeenCalledTimes(1)
|
||||
expect(moneyAmountRepository.insertBulk).toHaveBeenCalledWith([
|
||||
{
|
||||
variant_id: "ironman",
|
||||
variant: { id: IdMap.getId("ironman") },
|
||||
currency_code: "usd",
|
||||
amount: 4000,
|
||||
},
|
||||
@@ -761,7 +786,7 @@ describe("ProductVariantService", () => {
|
||||
|
||||
expect(moneyAmountRepository.create).toHaveBeenCalledTimes(1)
|
||||
expect(moneyAmountRepository.create).toHaveBeenCalledWith({
|
||||
variant_id: IdMap.getId("ironman"),
|
||||
variant: { id: IdMap.getId("ironman") },
|
||||
region_id: IdMap.getId("cali"),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { DeepPartial, EntityManager } from "typeorm"
|
||||
import { CustomerGroupService } from "."
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { CustomerGroup, PriceList, Product, ProductVariant } from "../models"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { PriceListRepository } from "../repositories/price-list"
|
||||
import { ProductVariantRepository } from "../repositories/product-variant"
|
||||
import { FindConfig, Selector } from "../types/common"
|
||||
import {
|
||||
CreatePriceListInput,
|
||||
FilterablePriceListProps,
|
||||
PriceListPriceCreateInput,
|
||||
PriceListPriceUpdateInput,
|
||||
UpdatePriceListInput,
|
||||
CreatePriceListInput,
|
||||
FilterablePriceListProps,
|
||||
PriceListPriceCreateInput,
|
||||
PriceListPriceUpdateInput,
|
||||
UpdatePriceListInput,
|
||||
} from "../types/price-list"
|
||||
import { CustomerGroup, PriceList, Product, ProductVariant } from "../models"
|
||||
import { DeepPartial, EntityManager } from "typeorm"
|
||||
import { FindConfig, Selector } from "../types/common"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
|
||||
import { CustomerGroupService } from "."
|
||||
import { FilterableProductProps } from "../types/product"
|
||||
import { FilterableProductVariantProps } from "../types/product-variant"
|
||||
import { buildQuery } from "../utils"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { PriceListRepository } from "../repositories/price-list"
|
||||
import ProductService from "./product"
|
||||
import { ProductVariantRepository } from "../repositories/product-variant"
|
||||
import ProductVariantService from "./product-variant"
|
||||
import RegionService from "./region"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { buildQuery } from "../utils"
|
||||
|
||||
type PriceListConstructorProps = {
|
||||
manager: EntityManager
|
||||
@@ -366,10 +367,12 @@ class PriceListService extends TransactionBaseService {
|
||||
requiresPriceList
|
||||
)
|
||||
|
||||
return productVariantRepo.create({
|
||||
const variant = productVariantRepo.create({
|
||||
...v,
|
||||
prices,
|
||||
})
|
||||
|
||||
variant.prices = prices
|
||||
return variant
|
||||
})
|
||||
)
|
||||
}
|
||||
@@ -497,13 +500,29 @@ class PriceListService extends TransactionBaseService {
|
||||
const regionServiceTx = this.regionService_.withTransaction(
|
||||
this.activeManager_
|
||||
)
|
||||
|
||||
const regions = await regionServiceTx.list(
|
||||
{
|
||||
id: [
|
||||
...new Set(
|
||||
prices
|
||||
.map((p) => p.region_id)
|
||||
.filter((p: string | undefined): p is string => !!p)
|
||||
),
|
||||
],
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
const regionsMap = new Map(regions.map((r) => [r.id, r]))
|
||||
|
||||
for (const price of prices) {
|
||||
const p = { ...price }
|
||||
|
||||
if (p.region_id) {
|
||||
const region = await regionServiceTx.retrieve(p.region_id)
|
||||
const region = regionsMap.get(p.region_id)
|
||||
|
||||
p.currency_code = region.currency_code
|
||||
p.currency_code = region!.currency_code
|
||||
}
|
||||
|
||||
prices_.push(p)
|
||||
|
||||
@@ -4,18 +4,18 @@ import { EntityManager } from "typeorm"
|
||||
import { ProductVariantService, RegionService, TaxProviderService } from "."
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import {
|
||||
IPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
IPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
} from "../interfaces/price-selection-strategy"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { Product, ProductVariant, Region, ShippingOption } from "../models"
|
||||
import {
|
||||
PricedProduct,
|
||||
PricedShippingOption,
|
||||
PricedVariant,
|
||||
PricingContext,
|
||||
ProductVariantPricing,
|
||||
TaxedPricing,
|
||||
PricedProduct,
|
||||
PricedShippingOption,
|
||||
PricedVariant,
|
||||
PricingContext,
|
||||
ProductVariantPricing,
|
||||
TaxedPricing,
|
||||
} from "../types/pricing"
|
||||
import { TaxServiceRate } from "../types/tax-service"
|
||||
import { calculatePriceTaxAmount } from "../utils"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import {
|
||||
Brackets,
|
||||
EntityManager,
|
||||
@@ -8,25 +7,8 @@ import {
|
||||
FindOptionsWhere,
|
||||
ILike,
|
||||
In,
|
||||
IsNull,
|
||||
SelectQueryBuilder,
|
||||
} from "typeorm"
|
||||
import {
|
||||
IPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
TransactionBaseService,
|
||||
} from "../interfaces"
|
||||
import {
|
||||
MoneyAmount,
|
||||
Product,
|
||||
ProductOptionValue,
|
||||
ProductVariant,
|
||||
} from "../models"
|
||||
import {
|
||||
FindWithRelationsOptions,
|
||||
ProductVariantRepository,
|
||||
} from "../repositories/product-variant"
|
||||
import { FindConfig, WithRequiredProperty } from "../types/common"
|
||||
import {
|
||||
CreateProductVariantInput,
|
||||
FilterableProductVariantProps,
|
||||
@@ -38,6 +20,23 @@ import {
|
||||
UpdateVariantPricesData,
|
||||
UpdateVariantRegionPriceData,
|
||||
} from "../types/product-variant"
|
||||
import { FindConfig, WithRequiredProperty } from "../types/common"
|
||||
import {
|
||||
FindWithRelationsOptions,
|
||||
ProductVariantRepository,
|
||||
} from "../repositories/product-variant"
|
||||
import {
|
||||
IPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
TransactionBaseService,
|
||||
} from "../interfaces"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import {
|
||||
MoneyAmount,
|
||||
Product,
|
||||
ProductOptionValue,
|
||||
ProductVariant,
|
||||
} from "../models"
|
||||
import {
|
||||
buildQuery,
|
||||
hasChanges,
|
||||
@@ -46,12 +45,12 @@ import {
|
||||
setMetadata,
|
||||
} from "../utils"
|
||||
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"
|
||||
import { CartRepository } from "../repositories/cart"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { ProductRepository } from "../repositories/product"
|
||||
import { ProductOptionValueRepository } from "../repositories/product-option-value"
|
||||
import EventBusService from "./event-bus"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { ProductOptionValueRepository } from "../repositories/product-option-value"
|
||||
import { ProductRepository } from "../repositories/product"
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"
|
||||
import RegionService from "./region"
|
||||
import { buildRelations } from "@medusajs/utils"
|
||||
|
||||
@@ -398,6 +397,7 @@ class ProductVariantService extends TransactionBaseService {
|
||||
{ id },
|
||||
{ id, ...toUpdate }
|
||||
)
|
||||
|
||||
result = variantRepo.create({
|
||||
...variant,
|
||||
...rawResult.generatedMaps[0],
|
||||
@@ -544,16 +544,16 @@ class ProductVariantService extends TransactionBaseService {
|
||||
const moneyAmountRepo = manager.withRepository(
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
const productVariantRepo = this.activeManager_.withRepository(
|
||||
this.productVariantRepository_
|
||||
)
|
||||
|
||||
const where = data.map((data_) => ({
|
||||
variant_id: data_.variantId,
|
||||
region_id: data_.price.region_id,
|
||||
price_list_id: IsNull(),
|
||||
}))
|
||||
|
||||
const moneyAmounts = await moneyAmountRepo.find({
|
||||
where,
|
||||
})
|
||||
const moneyAmounts = await moneyAmountRepo.findRegionMoneyAmounts(where)
|
||||
|
||||
const moneyAmountsMapToVariantId = new Map()
|
||||
moneyAmounts.map((d) => {
|
||||
@@ -582,12 +582,11 @@ class ProductVariantService extends TransactionBaseService {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
dataToCreate.push(
|
||||
moneyAmountRepo.create({
|
||||
...price,
|
||||
variant_id: variantId,
|
||||
}) as QueryDeepPartialEntity<MoneyAmount>
|
||||
)
|
||||
const ma = moneyAmountRepo.create({
|
||||
...price,
|
||||
}) as QueryDeepPartialEntity<MoneyAmount>
|
||||
ma.variant = { id: variantId }
|
||||
dataToCreate.push(ma)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -626,17 +625,16 @@ class ProductVariantService extends TransactionBaseService {
|
||||
const moneyAmountRepo = manager.withRepository(
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
const productVariantRepo = this.activeManager_.withRepository(
|
||||
this.productVariantRepository_
|
||||
)
|
||||
|
||||
const where = data.map((data_) => ({
|
||||
variant_id: data_.variantId,
|
||||
currency_code: data_.price.currency_code,
|
||||
region_id: IsNull(),
|
||||
price_list_id: IsNull(),
|
||||
}))
|
||||
|
||||
const moneyAmounts = await moneyAmountRepo.find({
|
||||
where,
|
||||
})
|
||||
const moneyAmounts = await moneyAmountRepo.findCurrencyMoneyAmounts(where)
|
||||
|
||||
const moneyAmountsMapToVariantId = new Map()
|
||||
moneyAmounts.map((d) => {
|
||||
@@ -665,13 +663,12 @@ class ProductVariantService extends TransactionBaseService {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
dataToCreate.push(
|
||||
moneyAmountRepo.create({
|
||||
...price,
|
||||
variant_id: variantId,
|
||||
currency_code: price.currency_code.toLowerCase(),
|
||||
}) as QueryDeepPartialEntity<MoneyAmount>
|
||||
)
|
||||
const ma = moneyAmountRepo.create({
|
||||
...price,
|
||||
currency_code: price.currency_code.toLowerCase(),
|
||||
}) as QueryDeepPartialEntity<MoneyAmount>
|
||||
ma.variant = { id: variantId }
|
||||
dataToCreate.push(ma)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -746,24 +743,34 @@ class ProductVariantService extends TransactionBaseService {
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
|
||||
let moneyAmount = await moneyAmountRepo.findOne({
|
||||
where: {
|
||||
variant_id: variantId,
|
||||
region_id: price.region_id,
|
||||
price_list_id: IsNull(),
|
||||
},
|
||||
})
|
||||
let [moneyAmount] = await moneyAmountRepo.getPricesForVariantInRegion(
|
||||
variantId,
|
||||
price.region_id
|
||||
)
|
||||
|
||||
const created = !moneyAmount
|
||||
|
||||
if (!moneyAmount) {
|
||||
moneyAmount = moneyAmountRepo.create({
|
||||
...price,
|
||||
variant_id: variantId,
|
||||
variant: { id: variantId },
|
||||
})
|
||||
} else {
|
||||
moneyAmount.amount = price.amount
|
||||
}
|
||||
|
||||
return await moneyAmountRepo.save(moneyAmount)
|
||||
const createdAmount = await moneyAmountRepo.save(moneyAmount)
|
||||
|
||||
if (created) {
|
||||
await moneyAmountRepo.createProductVariantMoneyAmounts([
|
||||
{
|
||||
variant_id: variantId,
|
||||
money_amount_id: createdAmount.id,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
return createdAmount
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { EntityManager } from "typeorm"
|
||||
import { computerizeAmount, MedusaError } from "medusa-core-utils"
|
||||
|
||||
import { AbstractBatchJobStrategy, IFileService } from "../../../interfaces"
|
||||
import CsvParser from "../../../services/csv-parser"
|
||||
import {
|
||||
BatchJobService,
|
||||
PriceListService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
} from "../../../services"
|
||||
import { CreateBatchJobInput } from "../../../types/batch-job"
|
||||
import {
|
||||
InjectedProps,
|
||||
OperationType,
|
||||
@@ -21,9 +16,14 @@ import {
|
||||
TBuiltPriceListImportLine,
|
||||
TParsedPriceListImportRowData,
|
||||
} from "./types"
|
||||
import { MedusaError, computerizeAmount } from "medusa-core-utils"
|
||||
|
||||
import { BatchJob } from "../../../models"
|
||||
import { TParsedProductImportRowData } from "../product/types"
|
||||
import { CreateBatchJobInput } from "../../../types/batch-job"
|
||||
import CsvParser from "../../../services/csv-parser"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { PriceListPriceCreateInput } from "../../../types/price-list"
|
||||
import { TParsedProductImportRowData } from "../product/types"
|
||||
|
||||
/*
|
||||
* Default strategy class used for a batch import of products/variants.
|
||||
@@ -309,12 +309,14 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
try {
|
||||
await txPriceListService.addPrices(
|
||||
priceListId,
|
||||
(op.prices as PriceListPriceCreateInput[]).map((p) => {
|
||||
return {
|
||||
...p,
|
||||
variant_id: op.variant_id,
|
||||
(op.prices as PriceListPriceCreateInput[]).map(
|
||||
(p: PriceListPriceCreateInput) => {
|
||||
return {
|
||||
...p,
|
||||
variant_id: op.variant_id as string,
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
} catch (e) {
|
||||
PriceListImportStrategy.throwDescriptiveError(op, e.message)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
AbstractPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
PriceSelectionResult,
|
||||
PriceType,
|
||||
AbstractPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
PriceSelectionResult,
|
||||
PriceType,
|
||||
} from "../interfaces"
|
||||
|
||||
import { ICacheService } from "@medusajs/types"
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
|
||||
@@ -158,6 +158,7 @@ export type PriceListPriceUpdateInput = {
|
||||
export type PriceListPriceCreateInput = {
|
||||
region_id?: string
|
||||
currency_code?: string
|
||||
variant_id: string
|
||||
amount: number
|
||||
min_quantity?: number
|
||||
max_quantity?: number
|
||||
|
||||
Reference in new issue
Block a user