diff --git a/database/schema.sql b/database/schema.sql index c4ac1b5..fca29f4 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -15,10 +15,16 @@ CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +-- ─── Schemas ──────────────────────────────────────────────────────────────── + +CREATE SCHEMA IF NOT EXISTS tenant; +CREATE SCHEMA IF NOT EXISTS masters; +CREATE SCHEMA IF NOT EXISTS cohort; + -- ─── Enum Types ───────────────────────────────────────────────────────────── DO $$ BEGIN - CREATE TYPE cohort_high_value_passenger_enum AS ENUM ( + CREATE TYPE cohort.cohort_high_value_passenger_enum AS ENUM ( 'Any', 'Yes(VIP/Strategic)', 'No' @@ -28,7 +34,7 @@ EXCEPTION END $$; DO $$ BEGIN - CREATE TYPE cohort_flight_type_enum AS ENUM ( + CREATE TYPE cohort.cohort_flight_type_enum AS ENUM ( 'Domestic Only', 'International Only', 'Both (All)' @@ -39,7 +45,7 @@ END $$; -- ─── Tenants ──────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS tbl_tenants ( +CREATE TABLE IF NOT EXISTS tenant.tbl_tenants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug VARCHAR NOT NULL, name VARCHAR NOT NULL, @@ -52,152 +58,152 @@ CREATE TABLE IF NOT EXISTS tbl_tenants ( -- ─── Master Data ──────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS tbl_membership_tiers ( +CREATE TABLE IF NOT EXISTS masters.tbl_membership_tiers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_membership_tiers_tenant ON tbl_membership_tiers("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_membership_tiers_tenant ON masters.tbl_membership_tiers("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_customer_values ( +CREATE TABLE IF NOT EXISTS masters.tbl_customer_values ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_customer_values_tenant ON tbl_customer_values("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_customer_values_tenant ON masters.tbl_customer_values("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_regions ( +CREATE TABLE IF NOT EXISTS masters.tbl_regions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_regions_tenant ON tbl_regions("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_regions_tenant ON masters.tbl_regions("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_trip_purposes ( +CREATE TABLE IF NOT EXISTS masters.tbl_trip_purposes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_trip_purposes_tenant ON tbl_trip_purposes("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_trip_purposes_tenant ON masters.tbl_trip_purposes("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_cabin_classes ( +CREATE TABLE IF NOT EXISTS masters.tbl_cabin_classes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_cabin_classes_tenant ON tbl_cabin_classes("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_cabin_classes_tenant ON masters.tbl_cabin_classes("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_passenger_types ( +CREATE TABLE IF NOT EXISTS masters.tbl_passenger_types ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_passenger_types_tenant ON tbl_passenger_types("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_passenger_types_tenant ON masters.tbl_passenger_types("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_ancillary_purchases ( +CREATE TABLE IF NOT EXISTS masters.tbl_ancillary_purchases ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_ancillary_purchases_tenant ON tbl_ancillary_purchases("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_ancillary_purchases_tenant ON masters.tbl_ancillary_purchases("tenantId"); -CREATE TABLE IF NOT EXISTS tbl_revenue_segments ( +CREATE TABLE IF NOT EXISTS masters.tbl_revenue_segments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, label VARCHAR NOT NULL, value VARCHAR NOT NULL, "isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_revenue_segments_tenant ON tbl_revenue_segments("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_revenue_segments_tenant ON masters.tbl_revenue_segments("tenantId"); -- ─── Cohorts ──────────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS tbl_cohorts ( +CREATE TABLE IF NOT EXISTS cohort.tbl_cohorts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - "tenantId" UUID NOT NULL REFERENCES tbl_tenants(id) ON DELETE CASCADE, + "tenantId" UUID NOT NULL REFERENCES tenant.tbl_tenants(id) ON DELETE CASCADE, "scopeType" VARCHAR, name VARCHAR NOT NULL, description VARCHAR, status VARCHAR NOT NULL DEFAULT 'Draft', - "highValuePassenger" cohort_high_value_passenger_enum NOT NULL DEFAULT 'Any', - "flightType" cohort_flight_type_enum, + "highValuePassenger" cohort.cohort_high_value_passenger_enum NOT NULL DEFAULT 'Any', + "flightType" cohort.cohort_flight_type_enum, "originAirport" TEXT, "destinationAirport" TEXT, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX IF NOT EXISTS idx_tbl_cohorts_tenant ON tbl_cohorts("tenantId"); +CREATE INDEX IF NOT EXISTS idx_tbl_cohorts_tenant ON cohort.tbl_cohorts("tenantId"); -- ─── Cohort Join Tables (Many-to-Many) ────────────────────────────────────── -CREATE TABLE IF NOT EXISTS tbl_cohort_cabin_classes ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "cabinClassId" UUID NOT NULL REFERENCES tbl_cabin_classes(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_cabin_classes ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "cabinClassId" UUID NOT NULL REFERENCES masters.tbl_cabin_classes(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "cabinClassId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_passenger_types ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "passengerTypeId" UUID NOT NULL REFERENCES tbl_passenger_types(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_passenger_types ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "passengerTypeId" UUID NOT NULL REFERENCES masters.tbl_passenger_types(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "passengerTypeId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_ancillary_purchases ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "ancillaryPurchaseId" UUID NOT NULL REFERENCES tbl_ancillary_purchases(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_ancillary_purchases ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "ancillaryPurchaseId" UUID NOT NULL REFERENCES masters.tbl_ancillary_purchases(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "ancillaryPurchaseId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_loyalty_tiers ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "membershipTierId" UUID NOT NULL REFERENCES tbl_membership_tiers(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_loyalty_tiers ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "membershipTierId" UUID NOT NULL REFERENCES masters.tbl_membership_tiers(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "membershipTierId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_revenue_segments ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "revenueSegmentId" UUID NOT NULL REFERENCES tbl_revenue_segments(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_revenue_segments ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "revenueSegmentId" UUID NOT NULL REFERENCES masters.tbl_revenue_segments(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "revenueSegmentId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_regions ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "regionId" UUID NOT NULL REFERENCES tbl_regions(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_regions ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "regionId" UUID NOT NULL REFERENCES masters.tbl_regions(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "regionId") ); -CREATE TABLE IF NOT EXISTS tbl_cohort_trip_purposes ( - "cohortId" UUID NOT NULL REFERENCES tbl_cohorts(id) ON DELETE CASCADE, - "tripPurposeId" UUID NOT NULL REFERENCES tbl_trip_purposes(id) ON DELETE CASCADE, +CREATE TABLE IF NOT EXISTS cohort.tbl_cohort_trip_purposes ( + "cohortId" UUID NOT NULL REFERENCES cohort.tbl_cohorts(id) ON DELETE CASCADE, + "tripPurposeId" UUID NOT NULL REFERENCES masters.tbl_trip_purposes(id) ON DELETE CASCADE, PRIMARY KEY ("cohortId", "tripPurposeId") ); diff --git a/src/database/database.module.ts b/src/database/database.module.ts index 5975070..5c91f2e 100644 --- a/src/database/database.module.ts +++ b/src/database/database.module.ts @@ -20,6 +20,27 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; ssl: configService.get('DB_SSL') === 'true' ? { rejectUnauthorized: false } : false, logging: false, }), + dataSourceFactory: async (options) => { + if (!options) { + throw new Error('Invalid options passed'); + } + const { DataSource } = await import('typeorm'); + // Initialize without syncing first + const dataSource = new DataSource({ ...options, synchronize: false }); + await dataSource.initialize(); + + // Create schemas if they don't exist + await dataSource.query(`CREATE SCHEMA IF NOT EXISTS "tenant";`); + await dataSource.query(`CREATE SCHEMA IF NOT EXISTS "masters";`); + await dataSource.query(`CREATE SCHEMA IF NOT EXISTS "cohort";`); + + // Run synchronization manually if it was enabled + if (options.synchronize) { + await dataSource.synchronize(); + } + + return dataSource; + }, }), ], }) diff --git a/src/modules/cohort/cohort.entity.ts b/src/modules/cohort/cohort.entity.ts index 3ee4288..3ef1273 100644 --- a/src/modules/cohort/cohort.entity.ts +++ b/src/modules/cohort/cohort.entity.ts @@ -8,7 +8,7 @@ import { RevenueSegment } from '../master-data/entities/revenue-segment.entity'; import { Region } from '../master-data/entities/region.entity'; import { TripPurpose } from '../master-data/entities/trip-purpose.entity'; -@Entity('tbl_cohorts') +@Entity({ name: 'tbl_cohorts', schema: 'cohort' }) export class Cohort extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/ancillary-purchase.entity.ts b/src/modules/master-data/entities/ancillary-purchase.entity.ts index e65c105..1f2a6d4 100644 --- a/src/modules/master-data/entities/ancillary-purchase.entity.ts +++ b/src/modules/master-data/entities/ancillary-purchase.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_ancillary_purchases') +@Entity({ name: 'tbl_ancillary_purchases', schema: 'masters' }) export class AncillaryPurchase extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/cabin-class.entity.ts b/src/modules/master-data/entities/cabin-class.entity.ts index 5fc5a98..fcdd2ce 100644 --- a/src/modules/master-data/entities/cabin-class.entity.ts +++ b/src/modules/master-data/entities/cabin-class.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_cabin_classes') +@Entity({ name: 'tbl_cabin_classes', schema: 'masters' }) export class CabinClass extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/customer-value.entity.ts b/src/modules/master-data/entities/customer-value.entity.ts index f4670fc..9842e28 100644 --- a/src/modules/master-data/entities/customer-value.entity.ts +++ b/src/modules/master-data/entities/customer-value.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_customer_values') +@Entity({ name: 'tbl_customer_values', schema: 'masters' }) export class CustomerValue extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/membership-tier.entity.ts b/src/modules/master-data/entities/membership-tier.entity.ts index 0651854..6a498b7 100644 --- a/src/modules/master-data/entities/membership-tier.entity.ts +++ b/src/modules/master-data/entities/membership-tier.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_membership_tiers') +@Entity({ name: 'tbl_membership_tiers', schema: 'masters' }) export class MembershipTier extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/passenger-type.entity.ts b/src/modules/master-data/entities/passenger-type.entity.ts index ba4bb96..cbd777b 100644 --- a/src/modules/master-data/entities/passenger-type.entity.ts +++ b/src/modules/master-data/entities/passenger-type.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_passenger_types') +@Entity({ name: 'tbl_passenger_types', schema: 'masters' }) export class PassengerType extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/region.entity.ts b/src/modules/master-data/entities/region.entity.ts index 92122f8..23c59ae 100644 --- a/src/modules/master-data/entities/region.entity.ts +++ b/src/modules/master-data/entities/region.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_regions') +@Entity({ name: 'tbl_regions', schema: 'masters' }) export class Region extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/revenue-segment.entity.ts b/src/modules/master-data/entities/revenue-segment.entity.ts index e815da7..729ef81 100644 --- a/src/modules/master-data/entities/revenue-segment.entity.ts +++ b/src/modules/master-data/entities/revenue-segment.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_revenue_segments') +@Entity({ name: 'tbl_revenue_segments', schema: 'masters' }) export class RevenueSegment extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/master-data/entities/trip-purpose.entity.ts b/src/modules/master-data/entities/trip-purpose.entity.ts index 27d2c6a..144655d 100644 --- a/src/modules/master-data/entities/trip-purpose.entity.ts +++ b/src/modules/master-data/entities/trip-purpose.entity.ts @@ -7,7 +7,7 @@ import { } from 'typeorm'; import { TenantOwnedEntity } from '../../../common/entities/tenant-owned.entity'; -@Entity('tbl_trip_purposes') +@Entity({ name: 'tbl_trip_purposes', schema: 'masters' }) export class TripPurpose extends TenantOwnedEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/src/modules/tenant/tenant.entity.ts b/src/modules/tenant/tenant.entity.ts index 67ce2ec..f971c78 100644 --- a/src/modules/tenant/tenant.entity.ts +++ b/src/modules/tenant/tenant.entity.ts @@ -6,7 +6,7 @@ import { UpdateDateColumn, } from 'typeorm'; -@Entity('tbl_tenants') +@Entity({ name: 'tbl_tenants', schema: 'tenant' }) export class Tenant { @PrimaryGeneratedColumn('uuid') id: string;