/** * seed-plans.ts * * Seeds all 4 subscription plan tiers with Vietnamese market pricing. * Called from prisma/seed.ts Phase 1 before any other data is inserted. * Idempotent — uses upsert on the unique `tier` field. * * Pricing (monthly / yearly): * FREE — 0 / 0 VND * AGENT_PRO — 499,000 / 4,990,000 VND (~$20/month, market-competitive) * INVESTOR — 999,000 / 9,990,000 VND * ENTERPRISE — 4,990,000 / 49,900,000 VND */ import { PrismaPg } from '@prisma/adapter-pg'; import { PrismaClient, PlanTier } from '@prisma/client'; import pg from 'pg'; const pool = new pg.Pool({ connectionString: process.env['DATABASE_URL'] }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); const PLANS: Array<{ id: string; tier: PlanTier; name: string; priceMonthlyVND: bigint; priceYearlyVND: bigint; maxListings: number | null; maxSavedSearches: number | null; maxAnalyticsQueries: number | null; maxReports: number | null; maxMediaUploads: number | null; featuredListingsQuota: number | null; features: Record; isActive: boolean; }> = [ { id: 'plan-free', tier: PlanTier.FREE, name: 'Miễn phí', priceMonthlyVND: 0n, priceYearlyVND: 0n, maxListings: 3, maxSavedSearches: 5, maxAnalyticsQueries: 0, maxReports: 0, maxMediaUploads: 15, // 3 listings × 5 photos featuredListingsQuota: 0, features: { basicSearch: true, listingPost: true, maxPhotos: 5, analytics: false, prioritySupport: false, aiValuation: false, featuredListing: false, leadManagement: false, agentProfile: false, marketReports: false, priceAlerts: false, portfolioTracking: false, apiAccess: false, whiteLabel: false, dedicatedSupport: false, }, isActive: true, }, { id: 'plan-agent-pro', tier: PlanTier.AGENT_PRO, name: 'Agent Pro', priceMonthlyVND: 499_000n, priceYearlyVND: 4_990_000n, maxListings: 50, maxSavedSearches: 30, maxAnalyticsQueries: 500, maxReports: 20, maxMediaUploads: 1_500, // 50 listings × 30 photos featuredListingsQuota: 5, features: { basicSearch: true, listingPost: true, maxPhotos: 30, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: true, leadManagement: true, agentProfile: true, marketReports: false, priceAlerts: false, portfolioTracking: false, apiAccess: false, whiteLabel: false, dedicatedSupport: false, }, isActive: true, }, { id: 'plan-investor', tier: PlanTier.INVESTOR, name: 'Investor', priceMonthlyVND: 999_000n, priceYearlyVND: 9_990_000n, maxListings: 20, maxSavedSearches: 100, maxAnalyticsQueries: 2_000, maxReports: 100, maxMediaUploads: 300, // 20 listings × 15 photos featuredListingsQuota: 0, features: { basicSearch: true, listingPost: true, maxPhotos: 15, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: false, leadManagement: false, agentProfile: false, marketReports: true, priceAlerts: true, portfolioTracking: true, apiAccess: false, whiteLabel: false, dedicatedSupport: false, }, isActive: true, }, { id: 'plan-enterprise', tier: PlanTier.ENTERPRISE, name: 'Enterprise', priceMonthlyVND: 4_990_000n, priceYearlyVND: 49_900_000n, maxListings: null, // unlimited maxSavedSearches: null, // unlimited maxAnalyticsQueries: null, // unlimited maxReports: null, // unlimited maxMediaUploads: null, // unlimited featuredListingsQuota: null, // unlimited features: { basicSearch: true, listingPost: true, maxPhotos: 100, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: true, leadManagement: true, agentProfile: true, marketReports: true, priceAlerts: true, portfolioTracking: true, apiAccess: true, whiteLabel: true, dedicatedSupport: true, }, isActive: true, }, ]; export async function seedPlans(): Promise { console.log('💳 Seeding subscription plans...'); for (const plan of PLANS) { await prisma.plan.upsert({ where: { tier: plan.tier }, update: { name: plan.name, priceMonthlyVND: plan.priceMonthlyVND, priceYearlyVND: plan.priceYearlyVND, maxListings: plan.maxListings, maxSavedSearches: plan.maxSavedSearches, maxAnalyticsQueries: plan.maxAnalyticsQueries, maxReports: plan.maxReports, maxMediaUploads: plan.maxMediaUploads, featuredListingsQuota: plan.featuredListingsQuota, features: plan.features, isActive: plan.isActive, }, create: { id: plan.id, tier: plan.tier, name: plan.name, priceMonthlyVND: plan.priceMonthlyVND, priceYearlyVND: plan.priceYearlyVND, maxListings: plan.maxListings, maxSavedSearches: plan.maxSavedSearches, maxAnalyticsQueries: plan.maxAnalyticsQueries, maxReports: plan.maxReports, maxMediaUploads: plan.maxMediaUploads, featuredListingsQuota: plan.featuredListingsQuota, features: plan.features, isActive: plan.isActive, }, }); } console.log(` ✓ ${PLANS.length} plans seeded (FREE, AGENT_PRO, INVESTOR, ENTERPRISE)`); } // Allow running standalone: `npx ts-node prisma/scripts/seed-plans.ts` if (require.main === module) { seedPlans() .catch((e) => { console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); await pool.end(); }); }