feat: Add currency module and remove currency models from region and pricing modules (#6536)

What:
- Creates a new currency module
- Removes currency model from the pricing module
- Removes currency model from region module
This commit is contained in:
Stevche Radevski authored and GitHub committed 2024-02-29 15:09:59 +00:00
1 parent 06f706a51a
commit dc025302a1
102 files changed
+1502 -2128

No files matched your search

@@ -26,12 +26,9 @@ describe("Region Module Service", () => {
await shutdownFunc()
})
it("should create countries and currencies on application start", async () => {
it("should create countries on application start", async () => {
const countries = await service.listCountries()
const currencies = await service.listCurrencies()
expect(countries.length).toBeGreaterThan(0)
expect(currencies.length).toBeGreaterThan(0)
})
it("should create countries added to default ones", async () => {
@@ -47,7 +44,7 @@ describe("Region Module Service", () => {
numeric: "420",
})
await service.createDefaultCountriesAndCurrencies()
await service.createDefaultCountries()
const [, newCount] = await service.listAndCountCountries()
expect(newCount).toEqual(initialCountries + 1)
@@ -69,7 +66,7 @@ describe("Region Module Service", () => {
)
const region = await service.retrieve(createdRegion.id, {
relations: ["currency", "countries"],
relations: ["countries"],
})
expect(region).toEqual(
@@ -77,10 +74,6 @@ describe("Region Module Service", () => {
id: region.id,
name: "Europe",
currency_code: "eur",
currency: expect.objectContaining({
code: "eur",
name: "Euro",
}),
countries: [],
})
)
@@ -94,7 +87,7 @@ describe("Region Module Service", () => {
})
const region = await service.retrieve(createdRegion.id, {
relations: ["countries", "currency"],
relations: ["countries"],
})
expect(region).toEqual(
@@ -102,10 +95,6 @@ describe("Region Module Service", () => {
id: region.id,
name: "North America",
currency_code: "usd",
currency: expect.objectContaining({
code: "usd",
name: "US Dollar",
}),
countries: [
expect.objectContaining({
display_name: "Canada",
@@ -182,7 +171,7 @@ describe("Region Module Service", () => {
})
const latestRegion = await service.retrieve(createdRegion.id, {
relations: ["currency", "countries"],
relations: ["countries"],
})
expect(latestRegion).toMatchObject({
@@ -243,7 +232,7 @@ describe("Region Module Service", () => {
})
const latestRegion = await service.retrieve(createdRegion.id, {
relations: ["currency", "countries"],
relations: ["countries"],
})
expect(latestRegion).toMatchObject({
@@ -268,7 +257,7 @@ describe("Region Module Service", () => {
})
const updatedRegion = await service.retrieve(createdRegion.id, {
relations: ["currency", "countries"],
relations: ["countries"],
})
expect(updatedRegion).toMatchObject({
@@ -294,7 +283,7 @@ describe("Region Module Service", () => {
})
const updatedRegion = await service.retrieve(createdRegion.id, {
relations: ["currency", "countries"],
relations: ["countries"],
})
expect(updatedRegion).toMatchObject({
@@ -306,23 +295,6 @@ describe("Region Module Service", () => {
expect(updatedRegion.countries).toHaveLength(0)
})
it("should fail updating the region currency to a non-existent one", async () => {
const createdRegion = await service.create({
name: "North America",
currency_code: "USD",
countries: ["us", "ca"],
})
await expect(
service.update(
{ id: createdRegion.id },
{
currency_code: "DOGECOIN",
}
)
).rejects.toThrowError('Currencies with codes: "dogecoin" were not found')
})
it("should fail updating the region countries to non-existent ones", async () => {
const createdRegion = await service.create({
name: "North America",
@@ -384,13 +356,4 @@ describe("Region Module Service", () => {
'Countries with codes: "mx" are already assigned to a region'
)
})
it("should fail when currency does not exist", async () => {
await expect(
service.create({
name: "Europe",
currency_code: "DOGECOIN",
})
).rejects.toThrowError('Currencies with codes: "dogecoin" were not found')
})
})
+1 -6
View File
@@ -1,11 +1,10 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModuleJoinerConfig } from "@medusajs/types"
import { MapToConfig } from "@medusajs/utils"
import { Country, Currency, Region } from "@models"
import { Country, Region } from "@models"
export const LinkableKeys = {
region_id: Region.name,
currency_code: Country.name,
country_id: Region.name,
}
@@ -29,10 +28,6 @@ export const joinerConfig: ModuleJoinerConfig = {
name: ["region", "regions"],
args: { entity: Region.name },
},
{
name: ["currency", "currencies"],
args: { entity: Currency.name },
},
{
name: ["country", "countries"],
args: { entity: Country.name },
+1 -1
View File
@@ -8,6 +8,6 @@ export default async ({ container }: LoaderOptions): Promise<void> => {
// TODO: Remove when legacy modules have been migrated
if (!!process.env.MEDUSA_FF_MEDUSA_V2) {
await service.createDefaultCountriesAndCurrencies()
await service.createDefaultCountries()
}
}
@@ -1,64 +1,7 @@
{
"namespaces": [
"public"
],
"namespaces": ["public"],
"name": "public",
"tables": [
{
"columns": {
"code": {
"name": "code",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"symbol": {
"name": "symbol",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"symbol_native": {
"name": "symbol_native",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"name": {
"name": "name",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
}
},
"name": "region_currency",
"schema": "public",
"indexes": [
{
"keyName": "region_currency_pkey",
"columnNames": [
"code"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
@@ -134,18 +77,7 @@
"schema": "public",
"indexes": [
{
"columnNames": [
"currency_code"
],
"composite": false,
"keyName": "IDX_region_currency_code",
"primary": false,
"unique": false
},
{
"columnNames": [
"deleted_at"
],
"columnNames": ["deleted_at"],
"composite": false,
"keyName": "IDX_region_deleted_at",
"primary": false,
@@ -153,30 +85,14 @@
},
{
"keyName": "region_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"region_currency_code_foreign": {
"constraintName": "region_currency_code_foreign",
"columnNames": [
"currency_code"
],
"localTableName": "public.region",
"referencedColumnNames": [
"code"
],
"referencedTableName": "public.region_currency",
"deleteRule": "set null",
"updateRule": "cascade"
}
}
"foreignKeys": {}
},
{
"columns": {
@@ -249,9 +165,7 @@
"indexes": [
{
"keyName": "region_country_pkey",
"columnNames": [
"id"
],
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true
@@ -261,13 +175,9 @@
"foreignKeys": {
"region_country_region_id_foreign": {
"constraintName": "region_country_region_id_foreign",
"columnNames": [
"region_id"
],
"columnNames": ["region_id"],
"localTableName": "public.region_country",
"referencedColumnNames": [
"id"
],
"referencedColumnNames": ["id"],
"referencedTableName": "public.region",
"deleteRule": "set null",
"updateRule": "cascade"
@@ -16,15 +16,6 @@ CREATE TABLE IF NOT EXISTS "region" (
CONSTRAINT "region_pkey" PRIMARY KEY ("id")
);
-- Create or update "region_currency" table
CREATE TABLE IF NOT EXISTS "region_currency" (
"code" text NOT NULL,
"symbol" text NOT NULL,
"symbol_native" text NOT NULL,
"name" text NOT NULL,
CONSTRAINT "region_currency_pkey" PRIMARY KEY ("code")
);
-- Adjust "region" table
ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_3bdd5896ec93be2f1c62a3309a5";
ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_91f88052197680f9790272aaf5b";
@@ -35,9 +26,6 @@ ${generatePostgresAlterColummnIfExistStatement(
"DROP NOT NULL"
)}
ALTER TABLE "region" ADD CONSTRAINT "region_currency_code_foreign" FOREIGN KEY ("currency_code") REFERENCES "region_currency" ("code") ON UPDATE CASCADE;
CREATE INDEX IF NOT EXISTS "IDX_region_currency_code" ON "region" ("currency_code");
CREATE INDEX IF NOT EXISTS "IDX_region_deleted_at" ON "region" ("deleted_at") WHERE "deleted_at" IS NOT NULL;
-- Create or update "region_country" table
-16
View File
@@ -1,16 +0,0 @@
import { Entity, PrimaryKey, Property } from "@mikro-orm/core"
@Entity({ tableName: "region_currency" })
export default class Currency {
@PrimaryKey({ columnType: "text" })
code: string
@Property({ columnType: "text" })
symbol: string
@Property({ columnType: "text" })
symbol_native: string
@Property({ columnType: "text" })
name: string
}
-2
View File
@@ -1,4 +1,2 @@
export { default as Country } from "./country"
export { default as Currency } from "./currency"
export { default as Region } from "./region"
+1 -12
View File
@@ -14,12 +14,8 @@ import {
Property,
} from "@mikro-orm/core"
import Country from "./country"
import Currency from "./currency"
type RegionOptionalProps =
| "currency"
| "countries"
| DAL.SoftDeletableEntityDateColumns
type RegionOptionalProps = "countries" | DAL.SoftDeletableEntityDateColumns
@Entity({ tableName: "region" })
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
@@ -35,13 +31,6 @@ export default class Region {
@Property({ columnType: "text" })
currency_code: string
@ManyToOne({
entity: () => Currency,
index: "IDX_region_currency_code",
nullable: true,
})
currency?: Currency
@OneToMany(() => Country, (country) => country.region)
countries = new Collection<Country>(this)
+9 -95
View File
@@ -8,7 +8,6 @@ import {
ModuleJoinerConfig,
ModulesSdkTypes,
RegionCountryDTO,
RegionCurrencyDTO,
RegionDTO,
UpdateRegionDTO,
UpsertRegionDTO,
@@ -27,9 +26,9 @@ import {
getDuplicates,
} from "@medusajs/utils"
import { Country, Currency, Region } from "@models"
import { Country, Region } from "@models"
import { CreateCountryDTO, CreateCurrencyDTO, UpdateRegionInput } from "@types"
import { CreateCountryDTO, UpdateRegionInput } from "@types"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
const COUNTRIES_LIMIT = 1000
@@ -38,15 +37,13 @@ type InjectedDependencies = {
baseRepository: DAL.RepositoryService
regionService: ModulesSdkTypes.InternalModuleService<any>
countryService: ModulesSdkTypes.InternalModuleService<any>
currencyService: ModulesSdkTypes.InternalModuleService<any>
}
const generateMethodForModels = [Country, Currency]
const generateMethodForModels = [Country]
export default class RegionModuleService<
TRegion extends Region = Region,
TCountry extends Country = Country,
TCurrency extends Currency = Currency
TCountry extends Country = Country
>
extends ModulesSdkUtils.abstractModuleServiceFactory<
InjectedDependencies,
@@ -55,9 +52,6 @@ export default class RegionModuleService<
Country: {
dto: RegionCountryDTO
}
Currency: {
dto: RegionCurrencyDTO
}
}
>(Region, generateMethodForModels, entityNameToLinkableKeysMap)
implements IRegionModuleService
@@ -65,15 +59,9 @@ export default class RegionModuleService<
protected baseRepository_: DAL.RepositoryService
protected readonly regionService_: ModulesSdkTypes.InternalModuleService<TRegion>
protected readonly countryService_: ModulesSdkTypes.InternalModuleService<TCountry>
protected readonly currencyService_: ModulesSdkTypes.InternalModuleService<TCurrency>
constructor(
{
baseRepository,
regionService,
countryService,
currencyService,
}: InjectedDependencies,
{ baseRepository, regionService, countryService }: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
// @ts-ignore
@@ -81,7 +69,6 @@ export default class RegionModuleService<
this.baseRepository_ = baseRepository
this.regionService_ = regionService
this.countryService_ = countryService
this.currencyService_ = currencyService
}
__joinerConfig(): ModuleJoinerConfig {
@@ -118,10 +105,6 @@ export default class RegionModuleService<
let normalizedInput = RegionModuleService.normalizeInput(data)
const validations = [
this.validateCurrencies(
normalizedInput.map((r) => r.currency_code),
sharedContext
),
this.validateCountries(
normalizedInput.map((r) => r.countries ?? []).flat(),
sharedContext
@@ -129,7 +112,7 @@ export default class RegionModuleService<
] as const
// Assign the full country object so the ORM updates the relationship
const [, dbCountries] = await promiseAll(validations)
const [dbCountries] = await promiseAll(validations)
const dbCountriesMap = new Map(dbCountries.map((d) => [d.iso_2, d]))
let normalizedDbRegions = normalizedInput.map((region) =>
removeUndefined({
@@ -246,10 +229,6 @@ export default class RegionModuleService<
}
const validations = [
this.validateCurrencies(
normalizedInput.map((d) => d.currency_code),
sharedContext
),
this.validateCountries(
normalizedInput.map((d) => d.countries ?? []).flat(),
sharedContext
@@ -257,7 +236,7 @@ export default class RegionModuleService<
] as const
// Assign the full country object so the ORM updates the relationship
const [, dbCountries] = await promiseAll(validations)
const [dbCountries] = await promiseAll(validations)
const dbCountriesMap = new Map(dbCountries.map((d) => [d.iso_2, d]))
let normalizedDbRegions = normalizedInput.map((region) =>
removeUndefined({
@@ -280,41 +259,6 @@ export default class RegionModuleService<
)
}
private async validateCurrencies(
currencyCodes: (string | undefined)[] | undefined,
sharedContext: Context
): Promise<void> {
const normalizedCurrencyCodes = currencyCodes
?.filter((c) => c !== undefined)
.map((c) => c!.toLowerCase())
if (!normalizedCurrencyCodes?.length) {
return
}
const uniqueCurrencyCodes = Array.from(new Set(normalizedCurrencyCodes))
const dbCurrencies = await this.currencyService_.list(
{ code: uniqueCurrencyCodes },
{},
sharedContext
)
const dbCurrencyCodes = dbCurrencies.map((c) => c.code.toLowerCase())
if (uniqueCurrencyCodes.length !== dbCurrencyCodes.length) {
const missingCurrencies = arrayDifference(
uniqueCurrencyCodes,
dbCurrencyCodes
)
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Currencies with codes: "${missingCurrencies.join(
", "
)}" were not found`
)
}
}
private async validateCountries(
countries: string[] | undefined,
sharedContext: Context
@@ -369,13 +313,10 @@ export default class RegionModuleService<
}
@InjectManager("baseRepository_")
public async createDefaultCountriesAndCurrencies(
public async createDefaultCountries(
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await promiseAll([
await this.maybeCreateCountries(sharedContext),
await this.maybeCreateCurrencies(sharedContext),
])
await this.maybeCreateCountries(sharedContext)
}
@InjectTransactionManager("baseRepository_")
@@ -409,31 +350,4 @@ export default class RegionModuleService<
await this.countryService_.create(countsToCreate, sharedContext)
}
}
@InjectTransactionManager("baseRepository_")
private async maybeCreateCurrencies(
@MedusaContext() sharedContext: Context
): Promise<void> {
const [currency] = await this.currencyService_.list(
{},
{ select: ["code"], take: 1 },
sharedContext
)
let currsToCreate: CreateCurrencyDTO[] = []
if (!currency) {
currsToCreate = Object.entries(DefaultsUtils.defaultCurrencies).map(
([code, currency]) => ({
code: code.toLowerCase(),
symbol: currency.symbol,
symbol_native: currency.symbol_native,
name: currency.name,
})
)
}
if (currsToCreate.length) {
await this.currencyService_.create(currsToCreate, sharedContext)
}
}
}
-7
View File
@@ -10,13 +10,6 @@ export type UpdateCountryRegion = {
region_id: string
}
export type CreateCurrencyDTO = {
code: string
symbol: string
name: string
symbol_native: string
}
export type CreateCountryDTO = {
iso_2: string
iso_3: string