Files
cfw-autumn/server/tests/utils/productUtils.ts
2026-06-09 17:20:48 +01:00

224 lines
4.3 KiB
TypeScript

import {
type AppEnv,
customerProducts,
type CreateReward,
type CreateRewardProgram,
isUsagePrice,
} from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js";
import type { AutumnInt } from "@/external/autumn/autumnCli.js";
import { ProductService } from "@/internal/products/ProductService.js";
import { eq } from "drizzle-orm";
export const createProduct = async ({
db,
orgId,
env,
autumn,
product,
prefix,
createInStripe,
}: {
db: DrizzleCli;
orgId: string;
env: AppEnv;
autumn: AutumnInt;
product: any;
prefix?: string;
createInStripe?: boolean;
}) => {
try {
const products = await ProductService.listFull({
db,
orgId,
env,
returnAll: true,
inIds: [product.id],
});
// customer_products_internal_product_id_fkey has no ON DELETE CASCADE.
// Wipe referencing rows first so the product delete succeeds.
for (const prod of products) {
await db
.delete(customerProducts)
.where(eq(customerProducts.internal_product_id, prod.internal_id));
}
const batchDelete = [];
for (const prod of products) {
batchDelete.push(
ProductService.deleteByInternalId({
db,
internalId: prod.internal_id,
orgId,
env,
}),
);
}
await Promise.all(batchDelete);
} catch (error) {
console.error("Error deleting product", error);
// Ignore deletion errors (might have customers attached)
}
const clone = structuredClone(product);
if (typeof clone.items === "object") {
clone.items = Object.values(clone.items);
}
if (prefix) {
clone.id = `${prefix}_${clone.id}`;
clone.name = `${clone.name} ${prefix}`;
}
if (createInStripe === false) {
clone.create_in_stripe = false;
}
try {
await autumn.products.create(clone);
} catch (error: any) {
// If product already exists (race condition), silently continue
if (
error?.message?.includes("already exists") ||
error?.message?.includes("duplicate") ||
error?.code === "PRODUCT_EXISTS"
) {
return;
}
throw error;
}
};
export const createProducts = async ({
db,
orgId,
env,
autumn,
products,
prefix,
customerId,
createInStripe,
}: {
db: DrizzleCli;
orgId: string;
env: AppEnv;
autumn: AutumnInt;
products: any[];
prefix?: string;
customerId?: string;
createInStripe?: boolean;
}) => {
if (customerId) {
try {
await autumn.customers.delete(customerId);
} catch (error) {}
}
const batchCreate = [];
for (const product of products) {
batchCreate.push(
createProduct({ db, orgId, env, autumn, product, prefix, createInStripe }),
);
}
await Promise.all(batchCreate);
};
export const createReward = async ({
db,
orgId,
env,
autumn,
reward,
productId,
onlyUsage = false,
}: {
db: DrizzleCli;
orgId: string;
env: AppEnv;
autumn: AutumnInt;
reward: CreateReward;
productId?: string;
onlyUsage?: boolean;
}) => {
// Only fetch product if we need usage prices
if (onlyUsage && productId) {
const fullProduct = await ProductService.getFull({
db,
orgId,
env,
idOrInternalId: productId,
});
const usagePrices = fullProduct?.prices.filter((price) =>
isUsagePrice({ price }),
);
reward.discount_config!.price_ids = usagePrices?.map((price) => price.id);
} else if (productId) {
const fullProduct = await ProductService.getFull({
db,
orgId,
env,
idOrInternalId: productId,
});
reward.discount_config!.price_ids =
fullProduct?.prices.map((p) => p.id) ?? [];
}
try {
await autumn.rewards.delete(reward.id);
} catch (error) {}
await autumn.rewards.create(reward);
};
export const createReferralProgram = async ({
db,
orgId,
env,
autumn,
reward,
rewardProgram,
productId,
onlyUsage = false,
}: {
db: DrizzleCli;
orgId: string;
env: AppEnv;
autumn: AutumnInt;
reward: CreateReward;
rewardProgram: CreateRewardProgram;
productId?: string;
onlyUsage?: boolean;
}) => {
// Create reward first
await createReward({
db,
orgId,
env,
autumn,
reward,
productId,
onlyUsage,
});
// Create referral program (will fail if already exists, but that's ok)
try {
await autumn.rewardPrograms.create(rewardProgram);
} catch (error: any) {
// If program already exists (race condition), silently continue
if (
error?.message?.includes("already exists") ||
error?.message?.includes("duplicate") ||
error?.code === "REWARD_PROGRAM_EXISTS"
) {
return;
}
throw error;
}
};