fix(medusa): gift card values & taxes are calculated correctly (#2777)
* chore: tax_rate is added to giftcards * chore: minor * chore: update gift card tax calculations to look at giftCard.tax_rate * chore: gift card transactions use tax rate from gift card for legacy * fix: gift cart total check for transaction should check the length * chore: use tax exclusive cost + use giftcard tax rate for gctransactions + refactor * chore: fix integration test * chore: address issues brought up in comments * chore: move gift card creation as a part of order service on order placed * chore: add type handling for gift card creation * chore: fix specs * chore: use taxLines to calculate tax of a gift card * chore: specs for line items containing gift cards and without * chore: add integration specs + fix tax rate bug * chore: round totaling + add GC application specs * chore: cleanup trialables * chore: write migration script to backfill gift cards with null tax_rate * chore: update legacy totals service for gift cards * chore: add changeset * chore: address PR review changes * chore: fix tests based on new totals calc * chore: address review comments Co-authored-by: adrien2p <adrien.deperetti@gmail.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
10
packages/medusa/src/scripts/db-config.ts
Normal file
10
packages/medusa/src/scripts/db-config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const typeormConfig = {
|
||||
type: process.env.TYPEORM_CONNECTION,
|
||||
url: process.env.TYPEORM_URL,
|
||||
username: process.env.TYPEORM_USERNAME,
|
||||
password: process.env.TYPEORM_PASSWORD,
|
||||
database: process.env.TYPEORM_DATABASE,
|
||||
migrations: [process.env.TYPEORM_MIGRATIONS as string],
|
||||
entities: [process.env.TYPEORM_ENTITIES],
|
||||
logging: true,
|
||||
}
|
||||
@@ -11,18 +11,9 @@ import {
|
||||
import { DiscountConditionProduct } from "../models/discount-condition-product"
|
||||
import { DiscountRule } from "../models/discount-rule"
|
||||
import { DiscountConditionRepository } from "../repositories/discount-condition"
|
||||
dotenv.config()
|
||||
import { typeormConfig } from "./db-config"
|
||||
|
||||
const typeormConfig = {
|
||||
type: process.env.TYPEORM_CONNECTION,
|
||||
url: process.env.TYPEORM_URL,
|
||||
username: process.env.TYPEORM_USERNAME,
|
||||
password: process.env.TYPEORM_PASSWORD,
|
||||
database: process.env.TYPEORM_DATABASE,
|
||||
migrations: [process.env.TYPEORM_MIGRATIONS as string],
|
||||
entities: [process.env.TYPEORM_ENTITIES],
|
||||
logging: true,
|
||||
}
|
||||
dotenv.config()
|
||||
|
||||
const migrate = async function ({ typeormConfig }): Promise<void> {
|
||||
const connection = await createConnection(typeormConfig)
|
||||
|
||||
101
packages/medusa/src/scripts/gift-card-tax-rate-migration.ts
Normal file
101
packages/medusa/src/scripts/gift-card-tax-rate-migration.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import dotenv from "dotenv"
|
||||
import { createConnection, IsNull } from "typeorm"
|
||||
import Logger from "../loaders/logger"
|
||||
import { GiftCard } from "../models/gift-card"
|
||||
import { typeormConfig } from "./db-config"
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const BATCH_SIZE = 1000
|
||||
const migrationName = 'gift-card-tax-rate-migration'
|
||||
|
||||
Logger.info(`typeormConfig: ${JSON.stringify(typeormConfig)}`)
|
||||
|
||||
const migrate = async function ({ typeormConfig }): Promise<void> {
|
||||
const connection = await createConnection(typeormConfig)
|
||||
|
||||
await connection.transaction(async (manager) => {
|
||||
let offset = 0
|
||||
|
||||
// Get all the GiftCards where the gift_card.tax_rate is null
|
||||
const giftCardsCount = await manager
|
||||
.createQueryBuilder()
|
||||
.withDeleted()
|
||||
.from(GiftCard, "gc")
|
||||
.select("gc.id")
|
||||
.where("gc.tax_rate IS NULL")
|
||||
.getCount()
|
||||
|
||||
const totalBatches = Math.ceil(giftCardsCount / BATCH_SIZE)
|
||||
|
||||
if (totalBatches == 0) {
|
||||
Logger.info(`${migrationName}: No records to update, skipping migration!`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Logger.info(`${migrationName}: Running migration for ${giftCardsCount} GiftCards`)
|
||||
Logger.info(`${migrationName}: Running migration in ${totalBatches} batch(es) of ${BATCH_SIZE}`)
|
||||
|
||||
for (let batch = 1; batch <= totalBatches; batch++) {
|
||||
Logger.info(`${migrationName}: Starting batch ${batch} of ${totalBatches}`)
|
||||
|
||||
// Get all the GiftCards and its region where the gift_card.tax_rate is null
|
||||
const giftCardRegionRecords = await manager
|
||||
.createQueryBuilder()
|
||||
.withDeleted()
|
||||
.from(GiftCard, "gc")
|
||||
.select("gc.id, gc.region_id, gc.tax_rate, r.tax_rate as region_tax_rate")
|
||||
.innerJoin("region", "r", "gc.region_id = r.id")
|
||||
.where("gc.tax_rate IS NULL")
|
||||
.limit(BATCH_SIZE)
|
||||
.offset(offset)
|
||||
.getRawMany()
|
||||
|
||||
// Loop through each gift card record and update the value of gift_card.tax_rate
|
||||
// with region.tax_rate value
|
||||
giftCardRegionRecords.forEach(async (gcr) => {
|
||||
await manager
|
||||
.createQueryBuilder()
|
||||
.update(GiftCard)
|
||||
.set({ tax_rate: gcr.region_tax_rate })
|
||||
.where("id = :id", { id: gcr.id })
|
||||
.execute()
|
||||
})
|
||||
|
||||
offset += BATCH_SIZE
|
||||
|
||||
Logger.info(`${migrationName}: Finished batch ${batch} of ${totalBatches}`)
|
||||
}
|
||||
|
||||
const recordsFailedToBackfill = await manager
|
||||
.createQueryBuilder()
|
||||
.withDeleted()
|
||||
.from(GiftCard, "gc")
|
||||
.select("gc.id")
|
||||
.where("gc.tax_rate IS NULL")
|
||||
.getCount()
|
||||
|
||||
if (recordsFailedToBackfill == 0) {
|
||||
Logger.info(`${migrationName}: successfully ran for ${giftCardsCount} GiftCards`)
|
||||
} else {
|
||||
Logger.info(`${migrationName}: ${recordsFailedToBackfill} GiftCards have no tax_rate set`)
|
||||
Logger.info(`${migrationName}: 1. Check if all GiftCards have a region associated with it`)
|
||||
Logger.info(`${migrationName}: If not, they need to be associated with a region & re-run migration`)
|
||||
Logger.info(`${migrationName}: 2. Check if regions have a tax_rate added to it`)
|
||||
Logger.info(`${migrationName}: If regions intentionally have no tax_rate, this can be ignored`)
|
||||
Logger.info(`${migrationName}: If not, add a tax_rate to region & re-run migration`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
migrate({ typeormConfig })
|
||||
.then(() => {
|
||||
Logger.info("Database migration completed")
|
||||
process.exit()
|
||||
}).catch((err) => {
|
||||
Logger.error(`Database migration failed - ${JSON.stringify(err)}`)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
export default migrate
|
||||
@@ -3,18 +3,9 @@ import { createConnection, SelectQueryBuilder } from "typeorm"
|
||||
import Logger from "../loaders/logger"
|
||||
import { LineItem } from "../models/line-item"
|
||||
import { LineItemAdjustment } from "../models/line-item-adjustment"
|
||||
dotenv.config()
|
||||
import { typeormConfig } from "./db-config"
|
||||
|
||||
const typeormConfig = {
|
||||
type: process.env.TYPEORM_CONNECTION,
|
||||
url: process.env.TYPEORM_URL,
|
||||
username: process.env.TYPEORM_USERNAME,
|
||||
password: process.env.TYPEORM_PASSWORD,
|
||||
database: process.env.TYPEORM_DATABASE,
|
||||
migrations: [process.env.TYPEORM_MIGRATIONS as string],
|
||||
entities: [process.env.TYPEORM_ENTITIES],
|
||||
logging: true,
|
||||
}
|
||||
dotenv.config()
|
||||
|
||||
const migrate = async function({ typeormConfig }) {
|
||||
const connection = await createConnection(typeormConfig)
|
||||
|
||||
@@ -3,20 +3,10 @@ import { createConnection } from "typeorm"
|
||||
import Logger from "../loaders/logger"
|
||||
import { Product } from "../models/product"
|
||||
import { Store } from "../models/store"
|
||||
import { typeormConfig } from "./db-config"
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const typeormConfig = {
|
||||
type: process.env.TYPEORM_CONNECTION,
|
||||
url: process.env.TYPEORM_URL,
|
||||
username: process.env.TYPEORM_USERNAME,
|
||||
password: process.env.TYPEORM_PASSWORD,
|
||||
database: process.env.TYPEORM_DATABASE,
|
||||
migrations: [process.env.TYPEORM_MIGRATIONS as string],
|
||||
entities: [process.env.TYPEORM_ENTITIES],
|
||||
logging: true,
|
||||
}
|
||||
|
||||
const migrate = async function ({ typeormConfig }): Promise<void> {
|
||||
const connection = await createConnection(typeormConfig)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user