chore(): Module Internal Events (#13296)
* chore(): Ensure the product module emits all necessary events * chore(): Ensure the product module emits all necessary events * Update events tests * more events and fixes * more tests and category fixes * more tests and category fixes * Add todo * update updateProduct_ event emitting and adjust test * Adjust update products implementation to rely on already computed events * rm unnecessary update variants events * Fix formatting in changeset for product events * refactor: Manage event emitting automatically (WIP) * refactor: Manage event emitting automatically (WIP) * chore(api-key): Add missing emit events and refactoring * chore(cart): Add missing emit events and refactoring * chore(customer): Add missing emit events and refactoring * chore(fufillment, utils): Add missing emit events and refactoring * chore(fufillment, utils): Add missing emit events and refactoring * chore(inventory): Add missing emit events and refactoring * chore(notification): Add missing emit events and refactoring * chore(utils): Remove medusa service event handling legacy * chore(product): Add missing emit events and refactoring * chore(order): Add missing emit events and refactoring * chore(payment): Add missing emit events and refactoring * chore(pricing, util): Add missing emit events and refactoring, fix internal service upsertWithReplace event dispatching * chore(promotions): Add missing emit events and refactoring * chore(region): Add missing emit events and refactoring * chore(sales-channel): Add missing emit events and refactoring * chore(settings): Add missing emit events and refactoring * chore(stock-location): Add missing emit events and refactoring * chore(store): Add missing emit events and refactoring * chore(taxes): Add missing emit events and refactoring * chore(user): Add missing emit events and refactoring * fix unit tests * rm changeset for regeneration * Create changeset for Medusa.js patch updates Add a changeset for patch updates to multiple Medusa.js modules. * rm unused product event builders * address feedback * remove old changeset * fix event action for token generated * fix user module events * fix import * fix promotion events * add new module integration tests shard * fix medusa service * revert shard * fix event action * fix pipeline * fix pipeline --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
afe21741c4
commit
e8822f3e69
@@ -10,6 +10,7 @@ import {
|
||||
TaxTypes,
|
||||
} from "@medusajs/framework/types"
|
||||
import {
|
||||
EmitEvents,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
promiseAll,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { TaxProvider, TaxRate, TaxRateRule, TaxRegion } from "@models"
|
||||
import { TaxProviderService } from "@services"
|
||||
@@ -94,6 +94,7 @@ export default class TaxModuleService
|
||||
): Promise<TaxTypes.TaxRateDTO>
|
||||
|
||||
@InjectManager()
|
||||
@EmitEvents()
|
||||
// @ts-expect-error
|
||||
async createTaxRates(
|
||||
data: TaxTypes.CreateTaxRateDTO[] | TaxTypes.CreateTaxRateDTO,
|
||||
@@ -101,7 +102,12 @@ export default class TaxModuleService
|
||||
): Promise<TaxTypes.TaxRateDTO[] | TaxTypes.TaxRateDTO> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
const rates = await this.createTaxRates_(input, sharedContext)
|
||||
return Array.isArray(data) ? rates : rates[0]
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TaxTypes.TaxRateDTO[] | TaxTypes.TaxRateDTO
|
||||
>(rates)
|
||||
|
||||
return Array.isArray(data) ? serialized : serialized[0]
|
||||
}
|
||||
|
||||
@InjectTransactionManager()
|
||||
@@ -145,9 +151,7 @@ export default class TaxModuleService
|
||||
await this.taxRateRuleService_.create(rulesToCreate, sharedContext)
|
||||
}
|
||||
|
||||
return await this.baseRepository_.serialize<TaxTypes.TaxRateDTO[]>(rates, {
|
||||
populate: true,
|
||||
})
|
||||
return rates
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
@@ -170,6 +174,7 @@ export default class TaxModuleService
|
||||
): Promise<TaxTypes.TaxRateDTO[]>
|
||||
|
||||
@InjectManager()
|
||||
@EmitEvents()
|
||||
// @ts-expect-error
|
||||
async updateTaxRates(
|
||||
selector: string | string[] | TaxTypes.FilterableTaxRateProps,
|
||||
@@ -177,9 +182,11 @@ export default class TaxModuleService
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TaxTypes.TaxRateDTO | TaxTypes.TaxRateDTO[]> {
|
||||
const rates = await this.updateTaxRates_(selector, data, sharedContext)
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TaxTypes.TaxRateDTO[]
|
||||
>(rates, { populate: true })
|
||||
>(rates)
|
||||
|
||||
return isString(selector) ? serialized[0] : serialized
|
||||
}
|
||||
|
||||
@@ -282,15 +289,18 @@ export default class TaxModuleService
|
||||
sharedContext?: Context
|
||||
): Promise<TaxTypes.TaxRateDTO>
|
||||
|
||||
@InjectTransactionManager()
|
||||
@InjectManager()
|
||||
@EmitEvents()
|
||||
async upsertTaxRates(
|
||||
data: TaxTypes.UpsertTaxRateDTO | TaxTypes.UpsertTaxRateDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TaxTypes.TaxRateDTO | TaxTypes.TaxRateDTO[]> {
|
||||
const result = await this.taxRateService_.upsert(data, sharedContext)
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TaxTypes.TaxRateDTO[]
|
||||
>(result, { populate: true })
|
||||
>(result)
|
||||
|
||||
return Array.isArray(data) ? serialized : serialized[0]
|
||||
}
|
||||
|
||||
@@ -307,6 +317,7 @@ export default class TaxModuleService
|
||||
): Promise<TaxRegionDTO[]>
|
||||
|
||||
@InjectManager()
|
||||
@EmitEvents()
|
||||
// @ts-expect-error
|
||||
async createTaxRegions(
|
||||
data: TaxTypes.CreateTaxRegionDTO | TaxTypes.CreateTaxRegionDTO[],
|
||||
@@ -314,9 +325,15 @@ export default class TaxModuleService
|
||||
) {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
const result = await this.createTaxRegions_(input, sharedContext)
|
||||
return Array.isArray(data) ? result : result[0]
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TaxTypes.TaxRegionDTO[] | TaxTypes.TaxRegionDTO
|
||||
>(result)
|
||||
|
||||
return Array.isArray(data) ? serialized : serialized[0]
|
||||
}
|
||||
|
||||
@InjectTransactionManager()
|
||||
async createTaxRegions_(
|
||||
data: TaxTypes.CreateTaxRegionDTO[],
|
||||
sharedContext: Context = {}
|
||||
@@ -347,10 +364,7 @@ export default class TaxModuleService
|
||||
await this.createTaxRates(rates, sharedContext)
|
||||
}
|
||||
|
||||
return await this.baseRepository_.serialize<TaxTypes.TaxRegionDTO[]>(
|
||||
regions,
|
||||
{ populate: true }
|
||||
)
|
||||
return regions
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
@@ -365,6 +379,7 @@ export default class TaxModuleService
|
||||
): Promise<TaxTypes.TaxRateRuleDTO[]>
|
||||
|
||||
@InjectManager()
|
||||
@EmitEvents()
|
||||
// @ts-expect-error
|
||||
async createTaxRateRules(
|
||||
data: TaxTypes.CreateTaxRateRuleDTO | TaxTypes.CreateTaxRateRuleDTO[],
|
||||
@@ -372,7 +387,12 @@ export default class TaxModuleService
|
||||
) {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
const result = await this.createTaxRateRules_(input, sharedContext)
|
||||
return Array.isArray(data) ? result : result[0]
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TaxTypes.TaxRateRuleDTO[] | TaxTypes.TaxRateRuleDTO
|
||||
>(result)
|
||||
|
||||
return Array.isArray(data) ? serialized : serialized[0]
|
||||
}
|
||||
|
||||
@InjectTransactionManager()
|
||||
@@ -381,12 +401,7 @@ export default class TaxModuleService
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const rules = await this.taxRateRuleService_.create(data, sharedContext)
|
||||
return await this.baseRepository_.serialize<TaxTypes.TaxRateRuleDTO[]>(
|
||||
rules,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
return rules
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
@@ -419,30 +434,71 @@ export default class TaxModuleService
|
||||
return []
|
||||
}
|
||||
|
||||
const toReturn = await promiseAll(
|
||||
items.map(async (item) => {
|
||||
const regionIds = regions.map((r) => r.id)
|
||||
const rateQuery = this.getTaxRateQueryForItem(item, regionIds)
|
||||
const candidateRates = await this.taxRateService_.list(
|
||||
rateQuery,
|
||||
{
|
||||
relations: ["tax_region", "rules"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
const regionIds = regions.map((r) => r.id)
|
||||
|
||||
const applicableRates = await this.getTaxRatesForItem(
|
||||
item,
|
||||
candidateRates
|
||||
)
|
||||
// Collect all unique reference IDs for batch query
|
||||
const productIds = new Set<string>()
|
||||
const productTypeIds = new Set<string>()
|
||||
const shippingOptionIds = new Set<string>()
|
||||
|
||||
return {
|
||||
rates: applicableRates,
|
||||
item,
|
||||
items.forEach((item) => {
|
||||
if ("shipping_option_id" in item) {
|
||||
shippingOptionIds.add(item.shipping_option_id)
|
||||
} else {
|
||||
productIds.add(item.product_id)
|
||||
if (item.product_type_id) {
|
||||
productTypeIds.add(item.product_type_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Build comprehensive query for all items
|
||||
const ruleQueries = [
|
||||
...Array.from(productIds).map((id) => ({
|
||||
reference: "product",
|
||||
reference_id: id,
|
||||
})),
|
||||
...Array.from(productTypeIds).map((id) => ({
|
||||
reference: "product_type",
|
||||
reference_id: id,
|
||||
})),
|
||||
...Array.from(shippingOptionIds).map((id) => ({
|
||||
reference: "shipping_option",
|
||||
reference_id: id,
|
||||
})),
|
||||
]
|
||||
|
||||
const allCandidateRates = await this.taxRateService_.list(
|
||||
{
|
||||
$and: [
|
||||
{ tax_region_id: regionIds },
|
||||
{
|
||||
$or: [
|
||||
{ is_default: true },
|
||||
...(ruleQueries.length ? [{ rules: { $or: ruleQueries } }] : []),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
relations: ["tax_region", "rules"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const toReturn = items.map((item) => {
|
||||
const rateQuery = this.getTaxRateQueryForItem(item, regionIds)
|
||||
const candidateRates = allCandidateRates.filter((rate) =>
|
||||
this.rateMatchesQuery(rate, rateQuery)
|
||||
)
|
||||
const applicableRates = this.getTaxRatesForItem(item, candidateRates)
|
||||
|
||||
return {
|
||||
rates: applicableRates,
|
||||
item,
|
||||
}
|
||||
})
|
||||
|
||||
const taxLines = await this.getTaxLinesFromProvider(
|
||||
parentRegion.provider_id as string,
|
||||
toReturn,
|
||||
@@ -574,10 +630,10 @@ export default class TaxModuleService
|
||||
}
|
||||
}
|
||||
|
||||
private async getTaxRatesForItem(
|
||||
private getTaxRatesForItem(
|
||||
item: TaxTypes.TaxableItemDTO | TaxTypes.TaxableShippingDTO,
|
||||
rates: InferEntityType<typeof TaxRate>[]
|
||||
): Promise<InferEntityType<typeof TaxRate>[]> {
|
||||
): InferEntityType<typeof TaxRate>[] {
|
||||
if (!rates.length) {
|
||||
return []
|
||||
}
|
||||
@@ -609,7 +665,17 @@ export default class TaxModuleService
|
||||
private getTaxRateQueryForItem(
|
||||
item: TaxTypes.TaxableItemDTO | TaxTypes.TaxableShippingDTO,
|
||||
regionIds: string[]
|
||||
) {
|
||||
): {
|
||||
$and: {
|
||||
tax_region_id?: string[]
|
||||
$or?: {
|
||||
is_default?: boolean
|
||||
rules?: {
|
||||
$or: { reference: string; reference_id: string | undefined }[]
|
||||
}
|
||||
}[]
|
||||
}[]
|
||||
} {
|
||||
const isShipping = "shipping_option_id" in item
|
||||
let ruleQuery = isShipping
|
||||
? [
|
||||
@@ -637,6 +703,58 @@ export default class TaxModuleService
|
||||
}
|
||||
}
|
||||
|
||||
private rateMatchesQuery(
|
||||
rate: InferEntityType<typeof TaxRate>,
|
||||
query: {
|
||||
$and: {
|
||||
tax_region_id?: string[]
|
||||
$or?: {
|
||||
is_default?: boolean
|
||||
rules?: {
|
||||
$or: { reference: string; reference_id: string | undefined }[]
|
||||
}
|
||||
}[]
|
||||
}[]
|
||||
}
|
||||
): boolean {
|
||||
const { $and } = query
|
||||
const [regionCheck, ruleCheck] = $and
|
||||
|
||||
// Check region match
|
||||
if (!regionCheck.tax_region_id?.includes(rate.tax_region_id)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check rule match
|
||||
const { $or } = ruleCheck
|
||||
if (rate.is_default) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if any rule matches
|
||||
for (const ruleCondition of $or ?? []) {
|
||||
if (ruleCondition.is_default && rate.is_default) {
|
||||
return true
|
||||
}
|
||||
if (ruleCondition.rules) {
|
||||
const { $or: ruleQueries } = ruleCondition.rules
|
||||
for (const ruleQuery of ruleQueries) {
|
||||
if (
|
||||
[...(rate.rules ?? [])]?.some(
|
||||
(rule: InferEntityType<typeof TaxRateRule>) =>
|
||||
rule.reference === ruleQuery.reference &&
|
||||
rule.reference_id === ruleQuery.reference_id
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private checkRuleMatches(
|
||||
rate: InferEntityType<typeof TaxRate>,
|
||||
item: TaxTypes.TaxableItemDTO | TaxTypes.TaxableShippingDTO
|
||||
|
||||
Reference in New Issue
Block a user