diff --git a/src/modules/billing-usage/billing-usage.module.ts b/src/modules/billing-usage/billing-usage.module.ts index 69d63e4..847e406 100644 --- a/src/modules/billing-usage/billing-usage.module.ts +++ b/src/modules/billing-usage/billing-usage.module.ts @@ -53,6 +53,7 @@ export class BillingUsageModule { require('./entities/usage-tracking.entity').UsageTracking, require('./entities/invoice.entity').Invoice, require('./entities/invoice-item.entity').InvoiceItem, + require('./entities/plan-feature.entity').PlanFeature, ]; } } diff --git a/src/modules/billing-usage/entities/index.ts b/src/modules/billing-usage/entities/index.ts index 90d2d55..7dc4e22 100644 --- a/src/modules/billing-usage/entities/index.ts +++ b/src/modules/billing-usage/entities/index.ts @@ -6,3 +6,4 @@ export { Invoice, InvoiceStatus } from './invoice.entity'; export { InvoiceItem, InvoiceItemType } from './invoice-item.entity'; export { BillingPaymentMethod, PaymentProvider, PaymentMethodType } from './payment-method.entity'; export { BillingAlert, BillingAlertType, AlertSeverity, AlertStatus } from './billing-alert.entity'; +export { PlanFeature } from './plan-feature.entity'; diff --git a/src/modules/billing-usage/entities/plan-feature.entity.ts b/src/modules/billing-usage/entities/plan-feature.entity.ts new file mode 100644 index 0000000..2f1e30b --- /dev/null +++ b/src/modules/billing-usage/entities/plan-feature.entity.ts @@ -0,0 +1,61 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + Index, + ManyToOne, + JoinColumn, +} from 'typeorm'; +import { SubscriptionPlan } from './subscription-plan.entity.js'; + +/** + * PlanFeature Entity + * Maps to billing.plan_features DDL table + * Features disponibles por plan de suscripcion + * Propagated from template-saas HU-REFACT-005 + */ +@Entity({ schema: 'billing', name: 'plan_features' }) +@Index('idx_plan_features_plan', ['planId']) +@Index('idx_plan_features_key', ['featureKey']) +export class PlanFeature { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ type: 'uuid', nullable: false, name: 'plan_id' }) + planId: string; + + @Column({ type: 'varchar', length: 100, nullable: false, name: 'feature_key' }) + featureKey: string; + + @Column({ type: 'varchar', length: 255, nullable: false, name: 'feature_name' }) + featureName: string; + + @Column({ type: 'varchar', length: 100, nullable: true }) + category: string | null; + + @Column({ type: 'boolean', default: true }) + enabled: boolean; + + @Column({ type: 'jsonb', default: {} }) + configuration: Record; + + @Column({ type: 'text', nullable: true }) + description: string | null; + + @Column({ type: 'jsonb', default: {} }) + metadata: Record; + + // Relaciones + @ManyToOne(() => SubscriptionPlan, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'plan_id' }) + plan: SubscriptionPlan; + + // Timestamps + @CreateDateColumn({ name: 'created_at', type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' }) + updatedAt: Date; +} diff --git a/src/modules/feature-flags/entities/flag-evaluation.entity.ts b/src/modules/feature-flags/entities/flag-evaluation.entity.ts new file mode 100644 index 0000000..27a0218 --- /dev/null +++ b/src/modules/feature-flags/entities/flag-evaluation.entity.ts @@ -0,0 +1,53 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + Index, + ManyToOne, + JoinColumn, +} from 'typeorm'; +import { Flag } from './flag.entity.js'; + +/** + * FlagEvaluation Entity + * Maps to flags.flag_evaluations DDL table + * Historial de evaluaciones de feature flags para analytics + * Propagated from template-saas HU-REFACT-005 + */ +@Entity({ schema: 'flags', name: 'flag_evaluations' }) +@Index('idx_flag_evaluations_flag', ['flagId']) +@Index('idx_flag_evaluations_tenant', ['tenantId']) +@Index('idx_flag_evaluations_date', ['evaluatedAt']) +export class FlagEvaluation { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ type: 'uuid', nullable: false, name: 'flag_id' }) + flagId: string; + + @Column({ type: 'uuid', nullable: false, name: 'tenant_id' }) + tenantId: string; + + @Column({ type: 'uuid', nullable: true, name: 'user_id' }) + userId: string | null; + + @Column({ type: 'boolean', nullable: false }) + result: boolean; + + @Column({ type: 'varchar', length: 100, nullable: true }) + variant: string | null; + + @Column({ type: 'jsonb', default: {}, name: 'evaluation_context' }) + evaluationContext: Record; + + @Column({ type: 'varchar', length: 100, nullable: true, name: 'evaluation_reason' }) + evaluationReason: string | null; + + @Column({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP', name: 'evaluated_at' }) + evaluatedAt: Date; + + // Relaciones + @ManyToOne(() => Flag, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'flag_id' }) + flag: Flag; +} diff --git a/src/modules/feature-flags/entities/index.ts b/src/modules/feature-flags/entities/index.ts index 99afff4..c76811f 100644 --- a/src/modules/feature-flags/entities/index.ts +++ b/src/modules/feature-flags/entities/index.ts @@ -1,2 +1,3 @@ export { Flag } from './flag.entity'; export { TenantOverride } from './tenant-override.entity'; +export { FlagEvaluation } from './flag-evaluation.entity'; diff --git a/src/modules/feature-flags/feature-flags.module.ts b/src/modules/feature-flags/feature-flags.module.ts index 82e814b..7d3e09f 100644 --- a/src/modules/feature-flags/feature-flags.module.ts +++ b/src/modules/feature-flags/feature-flags.module.ts @@ -2,7 +2,7 @@ import { Router } from 'express'; import { DataSource } from 'typeorm'; import { FeatureFlagsService } from './services'; import { FeatureFlagsController } from './controllers'; -import { Flag, TenantOverride } from './entities'; +import { Flag, TenantOverride, FlagEvaluation } from './entities'; export interface FeatureFlagsModuleOptions { dataSource: DataSource; @@ -39,6 +39,6 @@ export class FeatureFlagsModule { } static getEntities(): Function[] { - return [Flag, TenantOverride]; + return [Flag, TenantOverride, FlagEvaluation]; } }