refactor(medusa): Migrate StoreService to TS (#1413)
This commit is contained in:
1 parent
5172b21d09
commit
a605622bcb
10 files changed
+333
-289
No files matched your search
@@ -22,10 +22,12 @@ describe("GET /admin/store", () => {
|
|||||||
|
|
||||||
it("calls service retrieve", () => {
|
it("calls service retrieve", () => {
|
||||||
expect(StoreServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
expect(StoreServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||||
expect(StoreServiceMock.retrieve).toHaveBeenCalledWith([
|
expect(StoreServiceMock.retrieve).toHaveBeenCalledWith({
|
||||||
"currencies",
|
relations: [
|
||||||
"default_currency",
|
"currencies",
|
||||||
])
|
"default_currency",
|
||||||
|
]
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
PaymentProviderService,
|
PaymentProviderService,
|
||||||
StoreService,
|
StoreService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
|
import { FulfillmentProvider, PaymentProvider, Store } from "../../../../models"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /store
|
* @oas [get] /store
|
||||||
@@ -30,7 +31,12 @@ export default async (req, res) => {
|
|||||||
const fulfillmentProviderService: FulfillmentProviderService =
|
const fulfillmentProviderService: FulfillmentProviderService =
|
||||||
req.scope.resolve("fulfillmentProviderService")
|
req.scope.resolve("fulfillmentProviderService")
|
||||||
|
|
||||||
const data = await storeService.retrieve(["currencies", "default_currency"])
|
const data = (await storeService.retrieve({
|
||||||
|
relations: ["currencies", "default_currency"],
|
||||||
|
})) as Store & {
|
||||||
|
payment_providers: PaymentProvider[]
|
||||||
|
fulfillment_providers: FulfillmentProvider[]
|
||||||
|
}
|
||||||
|
|
||||||
const paymentProviders = await paymentProviderService.list()
|
const paymentProviders = await paymentProviderService.list()
|
||||||
const fulfillmentProviders = await fulfillmentProviderService.list()
|
const fulfillmentProviders = await fulfillmentProviderService.list()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsArray, IsOptional, IsString } from "class-validator"
|
import { IsArray, IsOptional, IsString, IsObject } from "class-validator"
|
||||||
import { StoreService } from "../../../../services"
|
import { StoreService } from "../../../../services"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
@@ -75,4 +75,8 @@ export class AdminPostStoreReq {
|
|||||||
@IsString({ each: true })
|
@IsString({ each: true })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
currencies?: string[]
|
currencies?: string[]
|
||||||
|
|
||||||
|
@IsObject()
|
||||||
|
@IsOptional()
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Customer } from "../../../.."
|
|||||||
import CustomerService from "../../../../services/customer"
|
import CustomerService from "../../../../services/customer"
|
||||||
import PaymentProviderService from "../../../../services/payment-provider"
|
import PaymentProviderService from "../../../../services/payment-provider"
|
||||||
import StoreService from "../../../../services/store"
|
import StoreService from "../../../../services/store"
|
||||||
|
import { PaymentProvider } from "../../../../models"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /customers/me/payment-methods
|
* @oas [get] /customers/me/payment-methods
|
||||||
@@ -32,8 +33,6 @@ import StoreService from "../../../../services/store"
|
|||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = req.user.customer_id
|
const id = req.user.customer_id
|
||||||
|
|
||||||
const storeService: StoreService = req.scope.resolve("storeService")
|
|
||||||
|
|
||||||
const paymentProviderService: PaymentProviderService = req.scope.resolve(
|
const paymentProviderService: PaymentProviderService = req.scope.resolve(
|
||||||
"paymentProviderService"
|
"paymentProviderService"
|
||||||
)
|
)
|
||||||
@@ -42,15 +41,18 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const customer: Customer = await customerService.retrieve(id)
|
const customer: Customer = await customerService.retrieve(id)
|
||||||
|
|
||||||
const store = await storeService.retrieve(["payment_providers"])
|
const paymentProviders: PaymentProvider[] =
|
||||||
|
await paymentProviderService.list()
|
||||||
|
|
||||||
const methods = await Promise.all(
|
const methods = await Promise.all(
|
||||||
store.payment_providers.map(async (next: string) => {
|
paymentProviders.map(async (paymentProvider: PaymentProvider) => {
|
||||||
const provider = paymentProviderService.retrieveProvider(next)
|
const provider = paymentProviderService.retrieveProvider(
|
||||||
|
paymentProvider.id
|
||||||
|
)
|
||||||
|
|
||||||
const pMethods = await provider.retrieveSavedMethods(customer)
|
const pMethods = await provider.retrieveSavedMethods(customer)
|
||||||
return pMethods.map((m) => ({
|
return pMethods.map((m) => ({
|
||||||
provider_id: next,
|
provider_id: paymentProvider.id,
|
||||||
data: m,
|
data: m,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ describe("StoreService", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("successfully retrieve store", async () => {
|
it("successfully retrieve store", async () => {
|
||||||
await storeService.retrieve()
|
await storeService.retrieve().catch(() => void 0)
|
||||||
|
|
||||||
expect(storeRepository.findOne).toHaveBeenCalledTimes(1)
|
expect(storeRepository.findOne).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
@@ -92,7 +92,7 @@ describe("StoreService", () => {
|
|||||||
storeService.update({
|
storeService.update({
|
||||||
currencies: ["1cd", "usd"],
|
currencies: ["1cd", "usd"],
|
||||||
})
|
})
|
||||||
).rejects.toThrow("Invalid currency 1cd")
|
).rejects.toThrow("Currency with code 1cd does not exist")
|
||||||
|
|
||||||
expect(storeRepository.findOne).toHaveBeenCalledTimes(1)
|
expect(storeRepository.findOne).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
@@ -148,8 +148,6 @@ describe("StoreService", () => {
|
|||||||
await expect(storeService.addCurrency("1cd")).rejects.toThrow(
|
await expect(storeService.addCurrency("1cd")).rejects.toThrow(
|
||||||
"Currency 1cd not found"
|
"Currency 1cd not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(storeRepository.findOne).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails if currency already existis", async () => {
|
it("fails if currency already existis", async () => {
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ class RegionService extends BaseService {
|
|||||||
async validateCurrency_(currencyCode) {
|
async validateCurrency_(currencyCode) {
|
||||||
const store = await this.storeService_
|
const store = await this.storeService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(this.transactionManager_)
|
||||||
.retrieve(["currencies"])
|
.retrieve({ relations: ["currencies"] })
|
||||||
|
|
||||||
const storeCurrencies = store.currencies.map((curr) => curr.code)
|
const storeCurrencies = store.currencies.map((curr) => curr.code)
|
||||||
|
|
||||||
|
|||||||
@@ -1,271 +0,0 @@
|
|||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import { BaseService } from "medusa-interfaces"
|
|
||||||
import { currencies } from "../utils/currencies"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides layer to manipulate store settings.
|
|
||||||
* @extends BaseService
|
|
||||||
*/
|
|
||||||
class StoreService extends BaseService {
|
|
||||||
constructor({
|
|
||||||
manager,
|
|
||||||
storeRepository,
|
|
||||||
currencyRepository,
|
|
||||||
eventBusService,
|
|
||||||
}) {
|
|
||||||
super()
|
|
||||||
|
|
||||||
/** @private @const {EntityManager} */
|
|
||||||
this.manager_ = manager
|
|
||||||
|
|
||||||
/** @private @const {StoreRepository} */
|
|
||||||
this.storeRepository_ = storeRepository
|
|
||||||
|
|
||||||
/** @private @const {CurrencyRepository} */
|
|
||||||
this.currencyRepository_ = currencyRepository
|
|
||||||
|
|
||||||
/** @private @const {EventBus} */
|
|
||||||
this.eventBus_ = eventBusService
|
|
||||||
}
|
|
||||||
|
|
||||||
withTransaction(transactionManager) {
|
|
||||||
if (!transactionManager) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
const cloned = new StoreService({
|
|
||||||
manager: transactionManager,
|
|
||||||
storeRepository: this.storeRepository_,
|
|
||||||
currencyRepository: this.currencyRepository_,
|
|
||||||
eventBusService: this.eventBus_,
|
|
||||||
})
|
|
||||||
|
|
||||||
cloned.transactionManager_ = transactionManager
|
|
||||||
|
|
||||||
return cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a store if it doesn't already exist.
|
|
||||||
* @return {Promise<Store>} the store.
|
|
||||||
*/
|
|
||||||
async create() {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const storeRepository = manager.getCustomRepository(this.storeRepository_)
|
|
||||||
const currencyRepository = manager.getCustomRepository(
|
|
||||||
this.currencyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
let store = await this.retrieve()
|
|
||||||
|
|
||||||
if (!store) {
|
|
||||||
const s = await storeRepository.create()
|
|
||||||
// Add default currency (USD) to store currencies
|
|
||||||
const usd = await currencyRepository.findOne({
|
|
||||||
code: "usd",
|
|
||||||
})
|
|
||||||
|
|
||||||
if (usd) {
|
|
||||||
s.currencies = [usd]
|
|
||||||
}
|
|
||||||
|
|
||||||
store = await storeRepository.save(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the store settings. There is always a maximum of one store.
|
|
||||||
* @param {string[]} relations - relations to fetch with store
|
|
||||||
* @return {Promise<Store>} the store
|
|
||||||
*/
|
|
||||||
async retrieve(relations = []) {
|
|
||||||
const storeRepo = this.manager_.getCustomRepository(this.storeRepository_)
|
|
||||||
|
|
||||||
const store = await storeRepo.findOne({ relations })
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultCurrency_(code) {
|
|
||||||
const currencyObject = currencies[code.toUpperCase()]
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: currencyObject.code.toLowerCase(),
|
|
||||||
symbol: currencyObject.symbol,
|
|
||||||
symbol_native: currencyObject.symbol_native,
|
|
||||||
name: currencyObject.name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a store
|
|
||||||
* @param {object} update - an object with the update values.
|
|
||||||
* @return {Promise} resolves to the update result.
|
|
||||||
*/
|
|
||||||
async update(update) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const storeRepository = manager.getCustomRepository(this.storeRepository_)
|
|
||||||
const currencyRepository = manager.getCustomRepository(
|
|
||||||
this.currencyRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
const store = await this.retrieve(["currencies"])
|
|
||||||
|
|
||||||
const {
|
|
||||||
metadata,
|
|
||||||
default_currency_code,
|
|
||||||
currencies: storeCurrencies,
|
|
||||||
...rest
|
|
||||||
} = update
|
|
||||||
|
|
||||||
if (metadata) {
|
|
||||||
store.metadata = this.setMetadata_(store.id, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storeCurrencies) {
|
|
||||||
const defaultCurr = default_currency_code ?? store.default_currency_code
|
|
||||||
const hasDefCurrency = storeCurrencies.find(
|
|
||||||
(c) => c.toLowerCase() === defaultCurr.toLowerCase()
|
|
||||||
)
|
|
||||||
|
|
||||||
// throw if we are trying to remove a currency from store currently used as default
|
|
||||||
if (!hasDefCurrency) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`You are not allowed to remove default currency from store currencies without replacing it as well`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
store.currencies = await Promise.all(
|
|
||||||
storeCurrencies.map(async (curr) => {
|
|
||||||
const currency = await currencyRepository.findOne({
|
|
||||||
where: { code: curr.toLowerCase() },
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!currency) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Invalid currency ${curr}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return currency
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (default_currency_code) {
|
|
||||||
const storeCurrCodes = store.currencies.map((c) => c.code)
|
|
||||||
const hasDefCurrency = storeCurrCodes.find(
|
|
||||||
(c) => c === default_currency_code.toLowerCase()
|
|
||||||
)
|
|
||||||
|
|
||||||
// throw if store currencies does not have default currency
|
|
||||||
if (!hasDefCurrency) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Store does not have currency: ${default_currency_code}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const curr = await currencyRepository.findOne({
|
|
||||||
code: default_currency_code.toLowerCase(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!curr) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Currency ${default_currency_code} not found`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
store.default_currency = curr
|
|
||||||
store.default_currency_code = curr.code
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(rest)) {
|
|
||||||
store[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await storeRepository.save(store)
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a currency to the store
|
|
||||||
* @param {string} code - 3 character ISO currency code
|
|
||||||
* @return {Promise} result after update
|
|
||||||
*/
|
|
||||||
async addCurrency(code) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const storeRepo = manager.getCustomRepository(this.storeRepository_)
|
|
||||||
const currencyRepository = manager.getCustomRepository(
|
|
||||||
this.currencyRepository_
|
|
||||||
)
|
|
||||||
const store = await this.retrieve(["currencies"])
|
|
||||||
|
|
||||||
const curr = await currencyRepository.findOne({
|
|
||||||
where: { code: code.toLowerCase() },
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!curr) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Currency ${code} not found`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
store.currencies.map((c) => c.code).includes(curr.code.toLowerCase())
|
|
||||||
) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.DUPLICATE_ERROR,
|
|
||||||
`Currency already added`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
store.currencies = [...store.currencies, curr]
|
|
||||||
const updated = await storeRepo.save(store)
|
|
||||||
return updated
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a currency from the store
|
|
||||||
* @param {string} code - 3 character ISO currency code
|
|
||||||
* @return {Promise} result after update
|
|
||||||
*/
|
|
||||||
async removeCurrency(code) {
|
|
||||||
return this.atomicPhase_(async (manager) => {
|
|
||||||
const storeRepo = manager.getCustomRepository(this.storeRepository_)
|
|
||||||
const store = await this.retrieve(["currencies"])
|
|
||||||
|
|
||||||
const exists = store.currencies.find((c) => c.code === code.toLowerCase())
|
|
||||||
// If currency does not exist, return early
|
|
||||||
if (!exists) {
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
store.currencies = store.currencies.filter((c) => c.code !== code)
|
|
||||||
const updated = await storeRepo.save(store)
|
|
||||||
return updated
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorates a store object.
|
|
||||||
* @param {Store} store - the store to decorate.
|
|
||||||
* @param {string[]} fields - the fields to include.
|
|
||||||
* @param {string[]} expandFields - fields to expand.
|
|
||||||
* @return {Store} return the decorated Store.
|
|
||||||
*/
|
|
||||||
async decorate(store, fields, expandFields = []) {
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default StoreService
|
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { currencies, Currency } from "../utils/currencies"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import { StoreRepository } from "../repositories/store"
|
||||||
|
import { CurrencyRepository } from "../repositories/currency"
|
||||||
|
import EventBusService from "./event-bus"
|
||||||
|
import { Store } from "../models"
|
||||||
|
import { AdminPostStoreReq } from "../api/routes/admin/store"
|
||||||
|
import { FindConfig } from "../types/common"
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
|
import { buildQuery, setMetadata } from "../utils"
|
||||||
|
import { UpdateStoreInput } from "../types/store"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
manager: EntityManager
|
||||||
|
storeRepository: typeof StoreRepository
|
||||||
|
currencyRepository: typeof CurrencyRepository
|
||||||
|
eventBusService: EventBusService
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides layer to manipulate store settings.
|
||||||
|
* @extends BaseService
|
||||||
|
*/
|
||||||
|
class StoreService extends TransactionBaseService<StoreService> {
|
||||||
|
protected manager_: EntityManager
|
||||||
|
protected transactionManager_: EntityManager
|
||||||
|
|
||||||
|
protected readonly storeRepository_: typeof StoreRepository
|
||||||
|
protected readonly currencyRepository_: typeof CurrencyRepository
|
||||||
|
protected readonly eventBus_: EventBusService
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
manager,
|
||||||
|
storeRepository,
|
||||||
|
currencyRepository,
|
||||||
|
eventBusService,
|
||||||
|
}: InjectedDependencies) {
|
||||||
|
super({
|
||||||
|
manager,
|
||||||
|
storeRepository,
|
||||||
|
currencyRepository,
|
||||||
|
eventBusService,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.manager_ = manager
|
||||||
|
this.storeRepository_ = storeRepository
|
||||||
|
this.currencyRepository_ = currencyRepository
|
||||||
|
this.eventBus_ = eventBusService
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a store if it doesn't already exist.
|
||||||
|
* @return The store.
|
||||||
|
*/
|
||||||
|
async create(): Promise<Store> {
|
||||||
|
return await this.atomicPhase_(
|
||||||
|
async (transactionManager: EntityManager) => {
|
||||||
|
const storeRepository = transactionManager.getCustomRepository(
|
||||||
|
this.storeRepository_
|
||||||
|
)
|
||||||
|
const currencyRepository = transactionManager.getCustomRepository(
|
||||||
|
this.currencyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
let store = await this.retrieve().catch(() => void 0)
|
||||||
|
if (store) {
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
const newStore = await storeRepository.create()
|
||||||
|
// Add default currency (USD) to store currencies
|
||||||
|
const usd = await currencyRepository.findOne({
|
||||||
|
code: "usd",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (usd) {
|
||||||
|
newStore.currencies = [usd]
|
||||||
|
}
|
||||||
|
|
||||||
|
store = await storeRepository.save(newStore)
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the store settings. There is always a maximum of one store.
|
||||||
|
* @param config The config object from which the query will be built
|
||||||
|
* @return the store
|
||||||
|
*/
|
||||||
|
async retrieve(config: FindConfig<Store> = {}): Promise<Store> {
|
||||||
|
return await this.atomicPhase_(
|
||||||
|
async (transactionManager: EntityManager) => {
|
||||||
|
const storeRepo = transactionManager.getCustomRepository(
|
||||||
|
this.storeRepository_
|
||||||
|
)
|
||||||
|
const query = buildQuery({}, config)
|
||||||
|
const store = await storeRepo.findOne(query)
|
||||||
|
|
||||||
|
if (!store) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
"Store does not exist"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDefaultCurrency_(code: string): Partial<Currency> {
|
||||||
|
const currencyObject = currencies[code.toUpperCase()]
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: currencyObject.code.toLowerCase(),
|
||||||
|
symbol: currencyObject.symbol,
|
||||||
|
symbol_native: currencyObject.symbol_native,
|
||||||
|
name: currencyObject.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a store
|
||||||
|
* @param data - an object with the update values.
|
||||||
|
* @return resolves to the update result.
|
||||||
|
*/
|
||||||
|
async update(data: UpdateStoreInput): Promise<Store> {
|
||||||
|
return await this.atomicPhase_(
|
||||||
|
async (transactionManager: EntityManager) => {
|
||||||
|
const storeRepository = transactionManager.getCustomRepository(
|
||||||
|
this.storeRepository_
|
||||||
|
)
|
||||||
|
const currencyRepository = transactionManager.getCustomRepository(
|
||||||
|
this.currencyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const {
|
||||||
|
metadata,
|
||||||
|
default_currency_code,
|
||||||
|
currencies: storeCurrencies,
|
||||||
|
...rest
|
||||||
|
} = data
|
||||||
|
|
||||||
|
const store = await this.retrieve({ relations: ["currencies"] })
|
||||||
|
|
||||||
|
if (metadata) {
|
||||||
|
store.metadata = setMetadata(store, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storeCurrencies) {
|
||||||
|
const defaultCurr =
|
||||||
|
default_currency_code ?? store.default_currency_code
|
||||||
|
const hasDefCurrency = storeCurrencies.find(
|
||||||
|
(c) => c.toLowerCase() === defaultCurr.toLowerCase()
|
||||||
|
)
|
||||||
|
|
||||||
|
// throw if we are trying to remove a currency from store currently used as default
|
||||||
|
if (!hasDefCurrency) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`You are not allowed to remove default currency from store currencies without replacing it as well`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
store.currencies = await Promise.all(
|
||||||
|
storeCurrencies.map(async (curr) => {
|
||||||
|
const currency = await currencyRepository.findOne({
|
||||||
|
where: { code: curr.toLowerCase() },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!currency) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Currency with code ${curr} does not exist`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return currency
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default_currency_code) {
|
||||||
|
const hasDefCurrency = store.currencies.find(
|
||||||
|
(c) => c.code.toLowerCase() === default_currency_code.toLowerCase()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!hasDefCurrency) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Store does not have currency: ${default_currency_code}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const curr = (await currencyRepository.findOne({
|
||||||
|
code: default_currency_code.toLowerCase(),
|
||||||
|
})) as Currency
|
||||||
|
|
||||||
|
store.default_currency = curr
|
||||||
|
store.default_currency_code = curr.code
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(rest)) {
|
||||||
|
store[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return await storeRepository.save(store)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a currency to the store
|
||||||
|
* @param code - 3 character ISO currency code
|
||||||
|
* @return result after update
|
||||||
|
*/
|
||||||
|
async addCurrency(code: string): Promise<Store | never> {
|
||||||
|
return await this.atomicPhase_(
|
||||||
|
async (transactionManager: EntityManager) => {
|
||||||
|
const storeRepo = transactionManager.getCustomRepository(
|
||||||
|
this.storeRepository_
|
||||||
|
)
|
||||||
|
const currencyRepository = transactionManager.getCustomRepository(
|
||||||
|
this.currencyRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const curr = await currencyRepository.findOne({
|
||||||
|
where: { code: code.toLowerCase() },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!curr) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Currency ${code} not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = await this.retrieve({ relations: ["currencies"] })
|
||||||
|
|
||||||
|
const doesStoreInclCurrency = store.currencies
|
||||||
|
.map((c) => c.code.toLowerCase())
|
||||||
|
.includes(curr.code.toLowerCase())
|
||||||
|
if (doesStoreInclCurrency) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.DUPLICATE_ERROR,
|
||||||
|
`Currency already added`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
store.currencies = [...store.currencies, curr]
|
||||||
|
return await storeRepo.save(store)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a currency from the store
|
||||||
|
* @param code - 3 character ISO currency code
|
||||||
|
* @return result after update
|
||||||
|
*/
|
||||||
|
async removeCurrency(code: string): Promise<any> {
|
||||||
|
return await this.atomicPhase_(
|
||||||
|
async (transactionManager: EntityManager) => {
|
||||||
|
const storeRepo = transactionManager.getCustomRepository(
|
||||||
|
this.storeRepository_
|
||||||
|
)
|
||||||
|
const store = await this.retrieve({ relations: ["currencies"] })
|
||||||
|
const doesCurrencyExists = store.currencies.some(
|
||||||
|
(c) => c.code === code.toLowerCase()
|
||||||
|
)
|
||||||
|
if (!doesCurrencyExists) {
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
store.currencies = store.currencies.filter((c) => c.code !== code)
|
||||||
|
return await storeRepo.save(store)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreService
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export type UpdateStoreInput = {
|
||||||
|
name?: string
|
||||||
|
swap_link_template?: string
|
||||||
|
payment_link_template?: string
|
||||||
|
invite_link_template?: string
|
||||||
|
default_currency_code?: string
|
||||||
|
currencies?: string[]
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
}
|
||||||
@@ -1,4 +1,14 @@
|
|||||||
export const currencies = {
|
export type Currency = {
|
||||||
|
symbol: string
|
||||||
|
name: string
|
||||||
|
symbol_native: string
|
||||||
|
decimal_digits: number
|
||||||
|
rounding: number
|
||||||
|
code: string
|
||||||
|
name_plural: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const currencies: Record<string, Currency> = {
|
||||||
USD: {
|
USD: {
|
||||||
symbol: "$",
|
symbol: "$",
|
||||||
name: "US Dollar",
|
name: "US Dollar",
|
||||||
Reference in new issue
Block a user