feat(saas): implement marketplace and tenant onboarding
This commit is contained in:
@@ -299,35 +299,6 @@ internal sealed class CouponRedemptionConfiguration : IEntityTypeConfiguration<C
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantSubscriptionConfiguration : IEntityTypeConfiguration<TenantSubscription>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantSubscription> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_subscriptions");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.PlanCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.BillingCycle).HasMaxLength(50);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.ExpiresAt });
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantUsageRecordConfiguration : IEntityTypeConfiguration<TenantUsageRecord>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantUsageRecord> builder)
|
||||
{
|
||||
builder.ConfigureEntity("tenant_usage_records");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.MetricKey).HasMaxLength(100);
|
||||
builder.Property(entity => entity.MetricValue).HasPrecision(18, 4);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.MetricKey, entity.PeriodStart, entity.PeriodEnd });
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CommerceRefundRequestConfiguration : IEntityTypeConfiguration<CommerceRefundRequest>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<CommerceRefundRequest> builder)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Tiku.Domain.Catalog;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Operations;
|
||||
using Tiku.Domain.Platform;
|
||||
using Tiku.Domain.QuestionBanks;
|
||||
using Tiku.Domain.Tenancy;
|
||||
|
||||
@@ -89,10 +90,14 @@ internal sealed class BackendPermissionConfiguration : IEntityTypeConfiguration<
|
||||
builder.Property(entity => entity.Code).HasMaxLength(160);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.Area).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Module).HasMaxLength(100);
|
||||
builder.Property(entity => entity.PermissionModuleCode).HasMaxLength(120);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
||||
builder.HasIndex(entity => entity.Code).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.Area, entity.Module, entity.SortOrder });
|
||||
builder.HasIndex(entity => new { entity.Area, entity.PermissionModuleCode, entity.SortOrder });
|
||||
builder.HasOne<PermissionModule>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.PermissionModuleCode)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,76 +7,6 @@ using Tiku.Domain.Tenancy;
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Configurations;
|
||||
|
||||
internal sealed class PlatformSaasPlanConfiguration : IEntityTypeConfiguration<PlatformSaasPlan>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformSaasPlan> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_saas_plans");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Code).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.BillingCycle).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Currency).HasMaxLength(10);
|
||||
builder.Property(entity => entity.IncludedQuotas).IsJson("{}");
|
||||
builder.Property(entity => entity.OveragePrices).IsJson("{}");
|
||||
builder.Property(entity => entity.FeatureFlags).IsJson("{}");
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(entity => entity.Code).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.Status, entity.SortOrder });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_saas_plans_amount", "base_amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ProductModuleConfiguration : IEntityTypeConfiguration<ProductModule>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProductModule> builder)
|
||||
{
|
||||
builder.ConfigureEntity("product_modules");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Code).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(1000);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(entity => entity.Code).IsUnique();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlanModuleEntitlementConfiguration : IEntityTypeConfiguration<PlanModuleEntitlement>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlanModuleEntitlement> builder)
|
||||
{
|
||||
builder.ConfigureEntity("plan_module_entitlements");
|
||||
builder.Property(entity => entity.PlanCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.ModuleCode).HasMaxLength(100);
|
||||
builder.HasIndex(entity => new { entity.PlanCode, entity.ModuleCode }).IsUnique();
|
||||
builder.HasOne<PlatformSaasPlan>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.PlanCode)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<ProductModule>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.ModuleCode)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantModuleOverrideConfiguration : IEntityTypeConfiguration<TenantModuleOverride>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantModuleOverride> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_module_overrides");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ModuleCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Mode).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Reason).HasMaxLength(1000);
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.ModuleCode }).IsUnique();
|
||||
builder.HasOne<ProductModule>().WithMany()
|
||||
.HasPrincipalKey(entity => entity.Code)
|
||||
.HasForeignKey(entity => entity.ModuleCode)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantBillingProfileConfiguration : IEntityTypeConfiguration<TenantBillingProfile>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantBillingProfile> builder)
|
||||
@@ -98,84 +28,11 @@ internal sealed class TenantBillingProfileConfiguration : IEntityTypeConfigurati
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceConfiguration : IEntityTypeConfiguration<TenantInvoice>
|
||||
internal sealed class PlatformBillingInvoiceReminderConfiguration : IEntityTypeConfiguration<PlatformBillingInvoiceReminder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoice> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingInvoiceReminder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoices");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.InvoiceNo).HasMaxLength(100);
|
||||
builder.Property(entity => entity.InvoiceType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Currency).HasMaxLength(10);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.InvoiceNo }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.DueDate });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.BillingPeriodStart, entity.BillingPeriodEnd })
|
||||
.IsUnique()
|
||||
.HasFilter("invoice_type = 'usage_overage' and status <> 'void' and metadata->>'source' = 'usage_overage_auto'");
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoices_amounts", "subtotal_cents >= 0 and discount_cents >= 0 and tax_cents >= 0 and total_cents >= 0 and paid_cents >= 0 and balance_cents >= 0");
|
||||
table.HasCheckConstraint("ck_tenant_invoices_period", "billing_period_end is null or billing_period_start is null or billing_period_end >= billing_period_start");
|
||||
});
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceItemConfiguration : IEntityTypeConfiguration<TenantInvoiceItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoiceItem> builder)
|
||||
{
|
||||
builder.ConfigureEntity("tenant_invoice_items");
|
||||
builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
|
||||
builder.Property(entity => entity.ItemType).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Description).HasMaxLength(500);
|
||||
builder.Property(entity => entity.Quantity).HasPrecision(12, 2);
|
||||
builder.Property(entity => entity.Metadata).IsJson("{}");
|
||||
builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(entity => entity.InvoiceId);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoice_items_quantity", "quantity > 0");
|
||||
table.HasCheckConstraint("ck_tenant_invoice_items_amounts", "unit_amount_cents >= 0 and amount_cents >= 0");
|
||||
});
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoicePaymentConfiguration : IEntityTypeConfiguration<TenantInvoicePayment>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoicePayment> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoice_payments");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.PaymentNo).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Provider).HasMaxLength(50);
|
||||
builder.Property(entity => entity.Method).HasMaxLength(50);
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.ProviderTradeNo).HasMaxLength(200);
|
||||
builder.Property(entity => entity.RawPayload).IsJson("{}");
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.PaymentNo }).IsUnique();
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.Status });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_tenant_invoice_payments_amount", "amount_cents >= 0"));
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ReceivedBy).OnDelete(DeleteBehavior.SetNull);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantInvoiceReminderConfiguration : IEntityTypeConfiguration<TenantInvoiceReminder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantInvoiceReminder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_invoice_reminders");
|
||||
builder.ConfigureTenantEntity("platform_billing_invoice_reminders");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ReminderType).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Channel).HasSnakeCaseEnum();
|
||||
@@ -186,10 +43,10 @@ internal sealed class TenantInvoiceReminderConfiguration : IEntityTypeConfigurat
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.ReminderDate });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_invoice_reminders_level", "reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_tenant_invoice_reminders_balance", "balance_cents_snapshot >= 0");
|
||||
table.HasCheckConstraint("ck_platform_billing_invoice_reminders_level", "reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_billing_invoice_reminders_balance", "balance_cents_snapshot >= 0");
|
||||
});
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
builder.HasOne<PlatformBillingInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
@@ -243,11 +100,11 @@ internal sealed class PlatformAuditAlertConfiguration : IEntityTypeConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformDunningNotificationChannelConfiguration : IEntityTypeConfiguration<PlatformDunningNotificationChannel>
|
||||
internal sealed class PlatformBillingDunningNotificationChannelConfiguration : IEntityTypeConfiguration<PlatformBillingDunningNotificationChannel>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformDunningNotificationChannel> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingDunningNotificationChannel> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_dunning_notification_channels");
|
||||
builder.ConfigureEntity("platform_billing_dunning_notification_channels");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.ChannelCode).HasMaxLength(100);
|
||||
builder.Property(entity => entity.Name).HasMaxLength(200);
|
||||
@@ -262,17 +119,17 @@ internal sealed class PlatformDunningNotificationChannelConfiguration : IEntityT
|
||||
builder.HasIndex(entity => new { entity.Enabled, entity.MinReminderLevel, entity.ChannelCode });
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_platform_dunning_channels_level", "min_reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_dunning_channels_timeout", "timeout_seconds between 1 and 60");
|
||||
table.HasCheckConstraint("ck_platform_billing_dunning_channels_level", "min_reminder_level between 1 and 20");
|
||||
table.HasCheckConstraint("ck_platform_billing_dunning_channels_timeout", "timeout_seconds between 1 and 60");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformDunningNotificationEventConfiguration : IEntityTypeConfiguration<PlatformDunningNotificationEvent>
|
||||
internal sealed class PlatformBillingDunningNotificationEventConfiguration : IEntityTypeConfiguration<PlatformBillingDunningNotificationEvent>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformDunningNotificationEvent> builder)
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingDunningNotificationEvent> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_dunning_notification_events");
|
||||
builder.ConfigureTenantEntity("platform_billing_dunning_notification_events");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.Provider).HasSnakeCaseEnum();
|
||||
builder.Property(entity => entity.Status).HasSnakeCaseEnum();
|
||||
@@ -284,15 +141,20 @@ internal sealed class PlatformDunningNotificationEventConfiguration : IEntityTyp
|
||||
builder.HasIndex(entity => new { entity.ReminderId, entity.Status, entity.CreatedAt });
|
||||
builder.HasIndex(entity => new { entity.InvoiceId, entity.Status, entity.CreatedAt });
|
||||
builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.CreatedAt });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_dunning_events_attempts", "attempts >= 0"));
|
||||
builder.HasOne<PlatformDunningNotificationChannel>().WithMany().HasForeignKey(entity => entity.ChannelId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoiceReminder>().WithMany()
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_dunning_events_attempts", "attempts >= 0"));
|
||||
builder.HasOne<PlatformBillingDunningNotificationChannel>().WithMany()
|
||||
.HasForeignKey(entity => entity.ChannelId)
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_channel")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PlatformBillingInvoiceReminder>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.ReminderId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_reminder")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<TenantInvoice>().WithMany()
|
||||
builder.HasOne<PlatformBillingInvoice>().WithMany()
|
||||
.HasForeignKey(entity => new { entity.TenantId, entity.InvoiceId })
|
||||
.HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
|
||||
.HasConstraintName("fk_platform_billing_dunning_events_invoice")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Tiku.Domain.Platform;
|
||||
using Tiku.Domain.Tenancy;
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Configurations;
|
||||
|
||||
internal sealed class SaasFeatureConfiguration : IEntityTypeConfiguration<SaasFeature>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasFeature> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_features");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.Code).HasMaxLength(120);
|
||||
builder.Property(value => value.Name).HasMaxLength(200);
|
||||
builder.Property(value => value.Category).HasMaxLength(100);
|
||||
builder.Property(value => value.Description).HasMaxLength(1000);
|
||||
builder.Property(value => value.Currency).HasMaxLength(10);
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(value => value.Code).IsUnique();
|
||||
builder.HasIndex(value => new { value.Status, value.Category, value.SortOrder });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_saas_features_reference_price", "reference_price_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PermissionModuleConfiguration : IEntityTypeConfiguration<PermissionModule>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PermissionModule> builder)
|
||||
{
|
||||
builder.ConfigureEntity("permission_modules");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.Code).HasMaxLength(120);
|
||||
builder.Property(value => value.Name).HasMaxLength(200);
|
||||
builder.Property(value => value.Area).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.RequiredFeatureCode).HasMaxLength(120);
|
||||
builder.Property(value => value.Description).HasMaxLength(1000);
|
||||
builder.HasIndex(value => value.Code).IsUnique();
|
||||
builder.HasIndex(value => new { value.Area, value.SortOrder });
|
||||
builder.HasOne<SaasFeature>().WithMany()
|
||||
.HasPrincipalKey(value => value.Code)
|
||||
.HasForeignKey(value => value.RequiredFeatureCode)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SaasOfferingConfiguration : IEntityTypeConfiguration<SaasOffering>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasOffering> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_offerings");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.Code).HasMaxLength(120);
|
||||
builder.Property(value => value.Name).HasMaxLength(200);
|
||||
builder.Property(value => value.Type).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Description).HasMaxLength(1000);
|
||||
builder.HasIndex(value => value.Code).IsUnique();
|
||||
builder.HasIndex(value => new { value.Type, value.Status, value.SortOrder });
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SaasOfferingVersionConfiguration : IEntityTypeConfiguration<SaasOfferingVersion>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasOfferingVersion> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_offering_versions");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.BillingCycle).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Currency).HasMaxLength(10);
|
||||
builder.Property(value => value.Metadata).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.OfferingId, value.Version }).IsUnique();
|
||||
builder.HasIndex(value => new { value.OfferingId, value.Status, value.EffectiveAt });
|
||||
builder.HasOne<SaasOffering>().WithMany().HasForeignKey(value => value.OfferingId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_saas_offering_versions_version", "version > 0");
|
||||
table.HasCheckConstraint("ck_saas_offering_versions_amount", "original_amount_cents >= 0 and amount_cents >= 0 and amount_cents <= original_amount_cents");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SaasOfferingVersionFeatureConfiguration : IEntityTypeConfiguration<SaasOfferingVersionFeature>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasOfferingVersionFeature> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_offering_version_features");
|
||||
builder.Property(value => value.FeatureCode).HasMaxLength(120);
|
||||
builder.HasIndex(value => new { value.OfferingVersionId, value.FeatureCode }).IsUnique();
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.OfferingVersionId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<SaasFeature>().WithMany().HasPrincipalKey(value => value.Code).HasForeignKey(value => value.FeatureCode).OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SaasFeatureLimitDefinitionConfiguration : IEntityTypeConfiguration<SaasFeatureLimitDefinition>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasFeatureLimitDefinition> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_feature_limit_definitions");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.MetricCode).HasMaxLength(120);
|
||||
builder.Property(value => value.FeatureCode).HasMaxLength(120);
|
||||
builder.Property(value => value.Name).HasMaxLength(200);
|
||||
builder.Property(value => value.Unit).HasMaxLength(50);
|
||||
builder.Property(value => value.Kind).HasSnakeCaseEnum();
|
||||
builder.HasIndex(value => value.MetricCode).IsUnique();
|
||||
builder.HasIndex(value => new { value.FeatureCode, value.Kind });
|
||||
builder.HasOne<SaasFeature>().WithMany().HasPrincipalKey(value => value.Code).HasForeignKey(value => value.FeatureCode).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_saas_feature_limit_warning", "warning_percent between 1 and 100"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SaasOfferingVersionLimitConfiguration : IEntityTypeConfiguration<SaasOfferingVersionLimit>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SaasOfferingVersionLimit> builder)
|
||||
{
|
||||
builder.ConfigureEntity("saas_offering_version_limits");
|
||||
builder.Property(value => value.MetricCode).HasMaxLength(120);
|
||||
builder.HasIndex(value => new { value.OfferingVersionId, value.MetricCode }).IsUnique();
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.OfferingVersionId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<SaasFeatureLimitDefinition>().WithMany().HasPrincipalKey(value => value.MetricCode).HasForeignKey(value => value.MetricCode).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_saas_offering_version_limits_value", "limit_value >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantSaasSubscriptionConfiguration : IEntityTypeConfiguration<TenantSaasSubscription>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantSaasSubscription> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_saas_subscriptions");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.LifecycleVersion).IsConcurrencyToken();
|
||||
builder.Property(value => value.Metadata).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.Status, value.CurrentPeriodEnd });
|
||||
builder.HasIndex(value => value.TenantId).IsUnique().HasFilter("status in ('trial', 'active', 'past_due', 'suspended')");
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.BaseOfferingVersionId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.ScheduledBaseOfferingVersionId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_tenant_saas_subscriptions_period", "current_period_end > current_period_start and current_period_start >= starts_at"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantSaasSubscriptionItemConfiguration : IEntityTypeConfiguration<TenantSaasSubscriptionItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantSaasSubscriptionItem> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_saas_subscription_items");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.ItemType).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.HasIndex(value => new { value.TenantId, value.SubscriptionId, value.OfferingVersionId, value.Status });
|
||||
builder.HasIndex(value => new { value.TenantId, value.SubscriptionId, value.ItemType })
|
||||
.HasDatabaseName("ux_tenant_saas_subscription_items_current_base")
|
||||
.IsUnique()
|
||||
.HasFilter("item_type = 'base_plan' and status = 'active'");
|
||||
builder.HasIndex(value => new { value.TenantId, value.SubscriptionId, value.Status })
|
||||
.HasDatabaseName("ux_tenant_saas_subscription_items_scheduled_base")
|
||||
.IsUnique()
|
||||
.HasFilter("item_type = 'base_plan' and status = 'scheduled'");
|
||||
builder.HasOne<TenantSaasSubscription>().WithMany()
|
||||
.HasForeignKey(value => new { value.TenantId, value.SubscriptionId })
|
||||
.HasPrincipalKey(value => new { value.TenantId, value.Id })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.OfferingVersionId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<PlatformBillingOrderItem>().WithMany()
|
||||
.HasForeignKey(value => new { value.TenantId, value.SourceOrderItemId })
|
||||
.HasPrincipalKey(value => new { value.TenantId, value.Id })
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_tenant_saas_subscription_items_period", "ends_at > starts_at"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantFeatureOverrideConfiguration : IEntityTypeConfiguration<TenantFeatureOverride>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantFeatureOverride> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_feature_overrides");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.FeatureCode).HasMaxLength(120);
|
||||
builder.Property(value => value.Mode).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Reason).HasMaxLength(1000);
|
||||
builder.HasIndex(value => new { value.TenantId, value.FeatureCode }).IsUnique();
|
||||
builder.HasOne<SaasFeature>().WithMany().HasPrincipalKey(value => value.Code).HasForeignKey(value => value.FeatureCode).OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TenantFeatureUsageConfiguration : IEntityTypeConfiguration<TenantFeatureUsage>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TenantFeatureUsage> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("tenant_feature_usage");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.MetricCode).HasMaxLength(120);
|
||||
builder.Property(value => value.Version).IsConcurrencyToken();
|
||||
builder.HasIndex(value => new { value.TenantId, value.MetricCode, value.PeriodStart, value.PeriodEnd }).IsUnique();
|
||||
builder.HasOne<SaasFeatureLimitDefinition>().WithMany().HasPrincipalKey(value => value.MetricCode).HasForeignKey(value => value.MetricCode).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table =>
|
||||
{
|
||||
table.HasCheckConstraint("ck_tenant_feature_usage_values", "used_value >= 0 and limit_value_snapshot >= 0");
|
||||
table.HasCheckConstraint("ck_tenant_feature_usage_period", "period_end > period_start");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingQuoteConfiguration : IEntityTypeConfiguration<PlatformBillingQuote>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingQuote> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_billing_quotes");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.QuoteNo).HasMaxLength(100);
|
||||
builder.Property(value => value.IdempotencyKey).HasMaxLength(200);
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Purpose).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Currency).HasMaxLength(10);
|
||||
builder.Property(value => value.FeatureSnapshot).IsJson("[]");
|
||||
builder.Property(value => value.LimitSnapshot).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.QuoteNo }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.IdempotencyKey }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.Status, value.ExpiresAt });
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_quotes_amounts", "original_amount_cents >= 0 and discount_amount_cents >= 0 and total_amount_cents >= 0 and total_amount_cents = original_amount_cents - discount_amount_cents"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingQuoteItemConfiguration : IEntityTypeConfiguration<PlatformBillingQuoteItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingQuoteItem> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_billing_quote_items");
|
||||
builder.HasAlternateKey(value => new { value.TenantId, value.Id });
|
||||
builder.Property(value => value.ItemType).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Snapshot).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.QuoteId, value.OfferingVersionId }).IsUnique();
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(value => value.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PlatformBillingQuote>().WithMany().HasForeignKey(value => new { value.TenantId, value.QuoteId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.OfferingVersionId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_quote_items_amounts", "quantity > 0 and unit_amount_cents >= 0 and amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingOrderConfiguration : IEntityTypeConfiguration<PlatformBillingOrder>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingOrder> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_billing_orders");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.OrderNo).HasMaxLength(100);
|
||||
builder.Property(value => value.IdempotencyKey).HasMaxLength(200);
|
||||
builder.Property(value => value.Purpose).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Currency).HasMaxLength(10);
|
||||
builder.Property(value => value.Snapshot).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.OrderNo }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.IdempotencyKey }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.Status, value.CreatedAt });
|
||||
builder.HasOne<PlatformBillingQuote>().WithMany().HasForeignKey(value => new { value.TenantId, value.QuoteId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_orders_amounts", "original_amount_cents >= 0 and discount_amount_cents >= 0 and total_amount_cents >= 0 and total_amount_cents = original_amount_cents - discount_amount_cents"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingOrderItemConfiguration : IEntityTypeConfiguration<PlatformBillingOrderItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingOrderItem> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_billing_order_items");
|
||||
builder.HasAlternateKey(value => new { value.TenantId, value.Id });
|
||||
builder.Property(value => value.ItemType).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Snapshot).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.OrderId, value.OfferingVersionId }).IsUnique();
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(value => value.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PlatformBillingOrder>().WithMany().HasForeignKey(value => new { value.TenantId, value.OrderId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<SaasOfferingVersion>().WithMany().HasForeignKey(value => value.OfferingVersionId).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_order_items_amounts", "quantity > 0 and unit_amount_cents >= 0 and amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingPaymentConfiguration : IEntityTypeConfiguration<PlatformBillingPayment>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingPayment> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_billing_payments");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.PaymentNo).HasMaxLength(100);
|
||||
builder.Property(value => value.IdempotencyKey).HasMaxLength(200);
|
||||
builder.Property(value => value.Provider).HasMaxLength(50);
|
||||
builder.Property(value => value.Method).HasMaxLength(50);
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.ProviderTradeNo).HasMaxLength(200);
|
||||
builder.Property(value => value.ClientPayload).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.PaymentNo }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.IdempotencyKey }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.OrderId, value.Status });
|
||||
builder.HasOne<PlatformBillingOrder>().WithMany().HasForeignKey(value => new { value.TenantId, value.OrderId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_payments_amount", "amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingPaymentEventConfiguration : IEntityTypeConfiguration<PlatformBillingPaymentEvent>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingPaymentEvent> builder)
|
||||
{
|
||||
builder.ConfigureEntity("platform_billing_payment_events");
|
||||
builder.HasAlternateKey(value => new { value.TenantId, value.Id });
|
||||
builder.Property(value => value.Provider).HasMaxLength(50);
|
||||
builder.Property(value => value.ProviderEventId).HasMaxLength(200);
|
||||
builder.Property(value => value.EventType).HasMaxLength(100);
|
||||
builder.Property(value => value.Payload).IsJson("{}");
|
||||
builder.Property(value => value.CreatedAt).HasDefaultValueSql("now()");
|
||||
builder.HasIndex(value => new { value.TenantId, value.Provider, value.ProviderEventId }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.PaymentId, value.CreatedAt });
|
||||
builder.HasOne<Tenant>().WithMany().HasForeignKey(value => value.TenantId).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.HasOne<PlatformBillingPayment>().WithMany().HasForeignKey(value => new { value.TenantId, value.PaymentId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingRefundConfiguration : IEntityTypeConfiguration<PlatformBillingRefund>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingRefund> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_billing_refunds");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.RefundNo).HasMaxLength(100);
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Reason).HasMaxLength(1000);
|
||||
builder.Property(value => value.ProviderRefundNo).HasMaxLength(200);
|
||||
builder.HasIndex(value => new { value.TenantId, value.RefundNo }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.OrderId, value.Status });
|
||||
builder.HasOne<PlatformBillingOrder>().WithMany().HasForeignKey(value => new { value.TenantId, value.OrderId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.HasOne<PlatformBillingPayment>().WithMany().HasForeignKey(value => new { value.TenantId, value.PaymentId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_refunds_amount", "amount_cents > 0"));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlatformBillingInvoiceConfiguration : IEntityTypeConfiguration<PlatformBillingInvoice>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PlatformBillingInvoice> builder)
|
||||
{
|
||||
builder.ConfigureTenantEntity("platform_billing_invoices");
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(value => value.InvoiceNo).HasMaxLength(100);
|
||||
builder.Property(value => value.Status).HasSnakeCaseEnum();
|
||||
builder.Property(value => value.Currency).HasMaxLength(10);
|
||||
builder.Property(value => value.BillingProfileSnapshot).IsJson("{}");
|
||||
builder.HasIndex(value => new { value.TenantId, value.InvoiceNo }).IsUnique();
|
||||
builder.HasIndex(value => new { value.TenantId, value.Status, value.DueDate });
|
||||
builder.HasOne<PlatformBillingOrder>().WithMany().HasForeignKey(value => new { value.TenantId, value.OrderId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Restrict);
|
||||
builder.ToTable(table => table.HasCheckConstraint("ck_platform_billing_invoices_amount", "total_amount_cents >= 0"));
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,9 @@ internal sealed class TenantAuthPolicyConfiguration : IEntityTypeConfiguration<T
|
||||
builder.HasKey(entity => entity.TenantId);
|
||||
builder.ConfigureTimestamps();
|
||||
builder.Property(entity => entity.AllowExternalStudentSelfRegistration).HasDefaultValue(false);
|
||||
builder.Property(entity => entity.AllowedStudentLoginMethods)
|
||||
.HasColumnType("text[]")
|
||||
.HasDefaultValueSql("ARRAY['password']::text[]");
|
||||
builder.HasOne<Tenant>().WithOne()
|
||||
.HasForeignKey<TenantAuthPolicy>(entity => entity.TenantId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddCommerceAdjustmentVouchers : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "commerce_adjustment_vouchers",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
issue_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
batch_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
item_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
order_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
payment_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
refund_request_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
created_by = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
reviewed_by = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
voucher_no = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
direction = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
amount_cents = table.Column<int>(type: "integer", nullable: false),
|
||||
currency = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false),
|
||||
reason = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: false),
|
||||
proof_asset_key = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||
reviewed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
closed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_commerce_adjustment_vouchers", x => x.id);
|
||||
table.UniqueConstraint("ak_commerce_adjustment_vouchers_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_commerce_adjustment_vouchers_amount", "amount_cents > 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_commerce_reconciliation_batche~",
|
||||
columns: x => new { x.tenant_id, x.batch_id },
|
||||
principalTable: "commerce_reconciliation_batches",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_commerce_reconciliation_issues~",
|
||||
columns: x => new { x.tenant_id, x.issue_id },
|
||||
principalTable: "commerce_reconciliation_issues",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_commerce_reconciliation_items_~",
|
||||
columns: x => new { x.tenant_id, x.item_id },
|
||||
principalTable: "commerce_reconciliation_items",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_commerce_refund_requests_tenan~",
|
||||
columns: x => new { x.tenant_id, x.refund_request_id },
|
||||
principalTable: "commerce_refund_requests",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_orders_tenant_id_order_id",
|
||||
columns: x => new { x.tenant_id, x.order_id },
|
||||
principalTable: "orders",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_payments_tenant_id_payment_id",
|
||||
columns: x => new { x.tenant_id, x.payment_id },
|
||||
principalTable: "payments",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_users_created_by",
|
||||
column: x => x.created_by,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_vouchers_users_reviewed_by",
|
||||
column: x => x.reviewed_by,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "commerce_adjustment_voucher_events",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
voucher_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
from_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||
to_status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
actor_user_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
note = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
details = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_commerce_adjustment_voucher_events", x => x.id);
|
||||
table.UniqueConstraint("ak_commerce_adjustment_voucher_events_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_voucher_events_commerce_adjustment_vouc~",
|
||||
columns: x => new { x.tenant_id, x.voucher_id },
|
||||
principalTable: "commerce_adjustment_vouchers",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_voucher_events_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_commerce_adjustment_voucher_events_users_actor_user_id",
|
||||
column: x => x.actor_user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_voucher_events_actor_user_id",
|
||||
table: "commerce_adjustment_voucher_events",
|
||||
column: "actor_user_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_voucher_events_tenant_id_voucher_id_cre~",
|
||||
table: "commerce_adjustment_voucher_events",
|
||||
columns: new[] { "tenant_id", "voucher_id", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_created_by",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
column: "created_by");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_reviewed_by",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
column: "reviewed_by");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_batch_id",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "batch_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_issue_id_created_at",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "issue_id", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_item_id",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "item_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_order_id",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "order_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_payment_id",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "payment_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_refund_request_id",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "refund_request_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_status_created_at",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "status", "created_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_commerce_adjustment_vouchers_tenant_id_voucher_no",
|
||||
table: "commerce_adjustment_vouchers",
|
||||
columns: new[] { "tenant_id", "voucher_no" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "commerce_adjustment_voucher_events");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "commerce_adjustment_vouchers");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddVideoPlaybackProgress : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "video_playback_progress",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
video_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
question_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
position_seconds = table.Column<int>(type: "integer", nullable: false),
|
||||
duration_seconds = table.Column<int>(type: "integer", nullable: true),
|
||||
watched_seconds = table.Column<int>(type: "integer", nullable: false),
|
||||
is_completed = table.Column<bool>(type: "boolean", nullable: false),
|
||||
completed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
last_played_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
play_count = table.Column<int>(type: "integer", nullable: false),
|
||||
metadata = table.Column<JsonElement>(type: "jsonb", nullable: false, defaultValueSql: "'{}'::jsonb"),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_video_playback_progress", x => x.id);
|
||||
table.UniqueConstraint("ak_video_playback_progress_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.CheckConstraint("ck_video_playback_progress_duration", "duration_seconds is null or duration_seconds >= 0");
|
||||
table.CheckConstraint("ck_video_playback_progress_play_count", "play_count >= 0");
|
||||
table.CheckConstraint("ck_video_playback_progress_position", "position_seconds >= 0");
|
||||
table.CheckConstraint("ck_video_playback_progress_watched", "watched_seconds >= 0");
|
||||
table.ForeignKey(
|
||||
name: "fk_video_playback_progress_questions_tenant_id_question_id",
|
||||
columns: x => new { x.tenant_id, x.question_id },
|
||||
principalTable: "questions",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_video_playback_progress_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_video_playback_progress_users_user_id",
|
||||
column: x => x.user_id,
|
||||
principalTable: "users",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_video_playback_progress_video_explanations_tenant_id_video_~",
|
||||
columns: x => new { x.tenant_id, x.video_id },
|
||||
principalTable: "video_explanations",
|
||||
principalColumns: new[] { "tenant_id", "id" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_tenant_id_question_id",
|
||||
table: "video_playback_progress",
|
||||
columns: new[] { "tenant_id", "question_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_tenant_id_user_id_last_played_at",
|
||||
table: "video_playback_progress",
|
||||
columns: new[] { "tenant_id", "user_id", "last_played_at" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_tenant_id_user_id_video_id",
|
||||
table: "video_playback_progress",
|
||||
columns: new[] { "tenant_id", "user_id", "video_id" },
|
||||
unique: true,
|
||||
filter: "question_id is null");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_tenant_id_user_id_video_id_question~",
|
||||
table: "video_playback_progress",
|
||||
columns: new[] { "tenant_id", "user_id", "video_id", "question_id" },
|
||||
unique: true,
|
||||
filter: "question_id is not null");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_tenant_id_video_id",
|
||||
table: "video_playback_progress",
|
||||
columns: new[] { "tenant_id", "video_id" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_video_playback_progress_user_id",
|
||||
table: "video_playback_progress",
|
||||
column: "user_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "video_playback_progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemoveMfaAuthentication : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "mfa_satisfied",
|
||||
table: "auth_sessions");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "mfa_satisfied",
|
||||
table: "auth_sessions",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDistributedSecurityFoundation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddUniqueConstraint(
|
||||
name: "ak_platform_saas_plans_code",
|
||||
table: "platform_saas_plans",
|
||||
column: "code");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "inbox_state",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
message_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
consumer_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
lock_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
row_version = table.Column<byte[]>(type: "bytea", rowVersion: true, nullable: true),
|
||||
received = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
receive_count = table.Column<int>(type: "integer", nullable: false),
|
||||
expiration_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
consumed = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
delivered = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
last_sequence_number = table.Column<long>(type: "bigint", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_inbox_state", x => x.id);
|
||||
table.UniqueConstraint("ak_inbox_state_message_id_consumer_id", x => new { x.message_id, x.consumer_id });
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "outbox_state",
|
||||
columns: table => new
|
||||
{
|
||||
outbox_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
lock_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
row_version = table.Column<byte[]>(type: "bytea", rowVersion: true, nullable: true),
|
||||
created = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
delivered = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
last_sequence_number = table.Column<long>(type: "bigint", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_outbox_state", x => x.outbox_id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "product_modules",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
description = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
sort_order = table.Column<int>(type: "integer", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_product_modules", x => x.id);
|
||||
table.UniqueConstraint("ak_product_modules_code", x => x.code);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "tenant_auth_policies",
|
||||
columns: table => new
|
||||
{
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
allow_external_student_self_registration = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_tenant_auth_policies", x => x.tenant_id);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_auth_policies_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
// Existing tenants retain the historical external-login behavior. New tenants
|
||||
// receive an explicit fail-closed policy when created by PlatformAdminService.
|
||||
migrationBuilder.Sql("""
|
||||
INSERT INTO tenant_auth_policies
|
||||
(tenant_id, allow_external_student_self_registration, created_at, updated_at)
|
||||
SELECT id, TRUE, now(), now()
|
||||
FROM tenants
|
||||
ON CONFLICT (tenant_id) DO NOTHING;
|
||||
""");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "outbox_message",
|
||||
columns: table => new
|
||||
{
|
||||
sequence_number = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
enqueue_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||
sent_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
headers = table.Column<string>(type: "text", nullable: true),
|
||||
properties = table.Column<string>(type: "text", nullable: true),
|
||||
inbox_message_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
inbox_consumer_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
outbox_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
message_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
content_type = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||
message_type = table.Column<string>(type: "text", nullable: false),
|
||||
body = table.Column<string>(type: "text", nullable: false),
|
||||
conversation_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
correlation_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
initiator_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
request_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
source_address = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
destination_address = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
response_address = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
fault_address = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||
expiration_time = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_outbox_message", x => x.sequence_number);
|
||||
table.ForeignKey(
|
||||
name: "fk_outbox_message_inbox_state_inbox_message_id_inbox_consumer_~",
|
||||
columns: x => new { x.inbox_message_id, x.inbox_consumer_id },
|
||||
principalTable: "inbox_state",
|
||||
principalColumns: new[] { "message_id", "consumer_id" });
|
||||
table.ForeignKey(
|
||||
name: "fk_outbox_message_outbox_state_outbox_id",
|
||||
column: x => x.outbox_id,
|
||||
principalTable: "outbox_state",
|
||||
principalColumn: "outbox_id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "plan_module_entitlements",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
plan_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
module_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
enabled = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_plan_module_entitlements", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_plan_module_entitlements_platform_saas_plans_plan_code",
|
||||
column: x => x.plan_code,
|
||||
principalTable: "platform_saas_plans",
|
||||
principalColumn: "code",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "fk_plan_module_entitlements_product_modules_module_code",
|
||||
column: x => x.module_code,
|
||||
principalTable: "product_modules",
|
||||
principalColumn: "code",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "tenant_module_overrides",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
|
||||
module_code = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||
mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
reason = table.Column<string>(type: "character varying(1000)", maxLength: 1000, nullable: true),
|
||||
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
updated_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_tenant_module_overrides", x => x.id);
|
||||
table.UniqueConstraint("ak_tenant_module_overrides_tenant_id_id", x => new { x.tenant_id, x.id });
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_module_overrides_product_modules_module_code",
|
||||
column: x => x.module_code,
|
||||
principalTable: "product_modules",
|
||||
principalColumn: "code",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "fk_tenant_module_overrides_tenants_tenant_id",
|
||||
column: x => x.tenant_id,
|
||||
principalTable: "tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_inbox_state_delivered",
|
||||
table: "inbox_state",
|
||||
column: "delivered");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_outbox_message_enqueue_time",
|
||||
table: "outbox_message",
|
||||
column: "enqueue_time");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_outbox_message_expiration_time",
|
||||
table: "outbox_message",
|
||||
column: "expiration_time");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_outbox_message_inbox_message_id_inbox_consumer_id_sequence_~",
|
||||
table: "outbox_message",
|
||||
columns: new[] { "inbox_message_id", "inbox_consumer_id", "sequence_number" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_outbox_message_outbox_id_sequence_number",
|
||||
table: "outbox_message",
|
||||
columns: new[] { "outbox_id", "sequence_number" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_outbox_state_created",
|
||||
table: "outbox_state",
|
||||
column: "created");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_plan_module_entitlements_module_code",
|
||||
table: "plan_module_entitlements",
|
||||
column: "module_code");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_plan_module_entitlements_plan_code_module_code",
|
||||
table: "plan_module_entitlements",
|
||||
columns: new[] { "plan_code", "module_code" },
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_product_modules_code",
|
||||
table: "product_modules",
|
||||
column: "code",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_module_overrides_module_code",
|
||||
table: "tenant_module_overrides",
|
||||
column: "module_code");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_tenant_module_overrides_tenant_id_module_code",
|
||||
table: "tenant_module_overrides",
|
||||
columns: new[] { "tenant_id", "module_code" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "outbox_message");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "plan_module_entitlements");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenant_auth_policies");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenant_module_overrides");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "inbox_state");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "outbox_state");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "product_modules");
|
||||
|
||||
migrationBuilder.DropUniqueConstraint(
|
||||
name: "ak_platform_saas_plans_code",
|
||||
table: "platform_saas_plans");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,64 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Tiku.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class EnforceProductModuleCatalog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("""
|
||||
INSERT INTO product_modules
|
||||
(id, code, name, status, sort_order, created_at, updated_at)
|
||||
VALUES
|
||||
('10000000-0000-0000-0000-000000000001', 'dashboard', 'Dashboard', 'active', 10, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000002', 'staff', 'Staff', 'active', 20, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000003', 'role', 'Roles', 'active', 30, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000004', 'student', 'Students', 'active', 40, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000005', 'content', 'Content', 'active', 50, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000006', 'settings', 'Settings', 'active', 60, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000007', 'provider', 'Providers', 'active', 70, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000008', 'commerce', 'Commerce', 'active', 80, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-000000000009', 'crm', 'CRM', 'active', 90, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-00000000000a', 'commission', 'Commission', 'active', 100, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
|
||||
('10000000-0000-0000-0000-00000000000b', 'job', 'Background Jobs', 'active', 110, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (code) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
status = 'active',
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
updated_at = CURRENT_TIMESTAMP;
|
||||
|
||||
INSERT INTO plan_module_entitlements (id, plan_code, module_code, enabled)
|
||||
SELECT
|
||||
md5('plan-module:' || plan.code || ':' || module.code)::uuid,
|
||||
plan.code,
|
||||
module.code,
|
||||
TRUE
|
||||
FROM platform_saas_plans AS plan
|
||||
CROSS JOIN product_modules AS module
|
||||
WHERE module.code IN
|
||||
('dashboard', 'staff', 'role', 'student', 'content', 'settings',
|
||||
'provider', 'commerce', 'crm', 'commission', 'job')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM plan_module_entitlements AS existing
|
||||
WHERE existing.plan_code = plan.code)
|
||||
ON CONFLICT (plan_code, module_code) DO NOTHING;
|
||||
""");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("""
|
||||
DELETE FROM product_modules
|
||||
WHERE code IN
|
||||
('dashboard', 'staff', 'role', 'student', 'content', 'settings',
|
||||
'provider', 'commerce', 'crm', 'commission', 'job');
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,4 +15,17 @@ internal static class MigrationBuilderTenantGuardExtensions
|
||||
{
|
||||
migrationBuilder.Sql(PostgreSqlTenantConstraintSql.DropTenantGuards);
|
||||
}
|
||||
|
||||
public static void EnsureSaasCatalogGuards(this MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(PostgreSqlSaasCatalogConstraintSql.DropOfferingVersionImmutabilityGuards);
|
||||
migrationBuilder.Sql(PostgreSqlSaasCatalogConstraintSql.CreateOfferingVersionImmutabilityGuards);
|
||||
migrationBuilder.Sql(PostgreSqlSaasCatalogConstraintSql.CreateOfferingVersionChildrenImmutabilityGuards);
|
||||
migrationBuilder.Sql(PostgreSqlSaasCatalogConstraintSql.CreateSubscriptionOfferingTypeGuards);
|
||||
}
|
||||
|
||||
public static void DropSaasCatalogGuards(this MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(PostgreSqlSaasCatalogConstraintSql.DropOfferingVersionImmutabilityGuards);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
namespace Tiku.Infrastructure.Persistence;
|
||||
|
||||
internal static class PostgreSqlSaasCatalogConstraintSql
|
||||
{
|
||||
public const string CreateOfferingVersionImmutabilityGuards = """
|
||||
create or replace function tiku_guard_saas_offering_version_immutable()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
if tg_op = 'DELETE' then
|
||||
if old.status in ('published', 'retired') then
|
||||
raise exception 'published or retired SaaS offering versions cannot be deleted'
|
||||
using errcode = '23514',
|
||||
constraint = 'ck_saas_offering_versions_published_immutable';
|
||||
end if;
|
||||
|
||||
return old;
|
||||
end if;
|
||||
|
||||
if old.status not in ('published', 'retired') then
|
||||
return new;
|
||||
end if;
|
||||
|
||||
if old.status = 'published'
|
||||
and new.status = 'retired'
|
||||
and new.retired_at is not null
|
||||
and new.id is not distinct from old.id
|
||||
and new.offering_id is not distinct from old.offering_id
|
||||
and new.version is not distinct from old.version
|
||||
and new.billing_cycle is not distinct from old.billing_cycle
|
||||
and new.original_amount_cents is not distinct from old.original_amount_cents
|
||||
and new.amount_cents is not distinct from old.amount_cents
|
||||
and new.currency is not distinct from old.currency
|
||||
and new.effective_at is not distinct from old.effective_at
|
||||
and new.published_at is not distinct from old.published_at
|
||||
and new.metadata is not distinct from old.metadata
|
||||
and new.created_at is not distinct from old.created_at then
|
||||
return new;
|
||||
end if;
|
||||
|
||||
raise exception 'published or retired SaaS offering versions are immutable'
|
||||
using errcode = '23514',
|
||||
constraint = 'ck_saas_offering_versions_published_immutable';
|
||||
end;
|
||||
$$;
|
||||
|
||||
do $guard$
|
||||
begin
|
||||
if to_regclass('saas_offering_versions') is not null then
|
||||
execute 'create trigger trg_saas_offering_versions_published_immutable
|
||||
before update or delete on saas_offering_versions
|
||||
for each row execute function tiku_guard_saas_offering_version_immutable()';
|
||||
end if;
|
||||
end
|
||||
$guard$;
|
||||
""";
|
||||
|
||||
public const string CreateOfferingVersionChildrenImmutabilityGuards = """
|
||||
create or replace function tiku_guard_saas_offering_version_child_immutable()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
as $$
|
||||
declare
|
||||
version_status text;
|
||||
begin
|
||||
if tg_op = 'DELETE' then
|
||||
select status into version_status
|
||||
from saas_offering_versions
|
||||
where id = old.offering_version_id;
|
||||
|
||||
if version_status is distinct from 'draft' then
|
||||
raise exception 'published or retired SaaS offering version children are immutable'
|
||||
using errcode = '23514',
|
||||
constraint = 'ck_saas_offering_versions_published_immutable';
|
||||
end if;
|
||||
|
||||
return old;
|
||||
end if;
|
||||
|
||||
if tg_op = 'UPDATE' and old.offering_version_id <> new.offering_version_id then
|
||||
select status into version_status
|
||||
from saas_offering_versions
|
||||
where id = old.offering_version_id;
|
||||
|
||||
if version_status is distinct from 'draft' then
|
||||
raise exception 'published or retired SaaS offering version children are immutable'
|
||||
using errcode = '23514',
|
||||
constraint = 'ck_saas_offering_versions_published_immutable';
|
||||
end if;
|
||||
end if;
|
||||
|
||||
select status into version_status
|
||||
from saas_offering_versions
|
||||
where id = new.offering_version_id;
|
||||
|
||||
if version_status is distinct from 'draft' then
|
||||
raise exception 'published or retired SaaS offering version children are immutable'
|
||||
using errcode = '23514',
|
||||
constraint = 'ck_saas_offering_versions_published_immutable';
|
||||
end if;
|
||||
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
do $guard$
|
||||
begin
|
||||
if to_regclass('saas_offering_version_features') is not null then
|
||||
execute 'create trigger trg_saas_offering_version_features_published_immutable
|
||||
before insert or update or delete on saas_offering_version_features
|
||||
for each row execute function tiku_guard_saas_offering_version_child_immutable()';
|
||||
end if;
|
||||
|
||||
if to_regclass('saas_offering_version_limits') is not null then
|
||||
execute 'create trigger trg_saas_offering_version_limits_published_immutable
|
||||
before insert or update or delete on saas_offering_version_limits
|
||||
for each row execute function tiku_guard_saas_offering_version_child_immutable()';
|
||||
end if;
|
||||
end
|
||||
$guard$;
|
||||
""";
|
||||
|
||||
public const string CreateSubscriptionOfferingTypeGuards = """
|
||||
create or replace function tiku_guard_saas_subscription_offering_type()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
as $$
|
||||
declare
|
||||
offering_type text;
|
||||
source_version_id uuid;
|
||||
begin
|
||||
if tg_table_name = 'tenant_saas_subscriptions' then
|
||||
select o.type into offering_type
|
||||
from saas_offering_versions v
|
||||
join saas_offerings o on o.id = v.offering_id
|
||||
where v.id = new.base_offering_version_id;
|
||||
|
||||
if offering_type is distinct from 'base_plan' then
|
||||
raise exception 'subscription base offering version must belong to a base plan'
|
||||
using errcode = '23514', constraint = 'ck_tenant_saas_subscription_base_plan';
|
||||
end if;
|
||||
|
||||
if new.scheduled_base_offering_version_id is not null then
|
||||
select o.type into offering_type
|
||||
from saas_offering_versions v
|
||||
join saas_offerings o on o.id = v.offering_id
|
||||
where v.id = new.scheduled_base_offering_version_id;
|
||||
if offering_type is distinct from 'base_plan' then
|
||||
raise exception 'scheduled subscription offering version must belong to a base plan'
|
||||
using errcode = '23514', constraint = 'ck_tenant_saas_subscription_base_plan';
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end if;
|
||||
|
||||
select o.type into offering_type
|
||||
from saas_offering_versions v
|
||||
join saas_offerings o on o.id = v.offering_id
|
||||
where v.id = new.offering_version_id;
|
||||
if offering_type is distinct from new.item_type::text then
|
||||
raise exception 'subscription item type must match its offering type'
|
||||
using errcode = '23514', constraint = 'ck_tenant_saas_subscription_item_offering_type';
|
||||
end if;
|
||||
|
||||
if new.source_order_item_id is not null then
|
||||
select offering_version_id into source_version_id
|
||||
from platform_billing_order_items
|
||||
where tenant_id = new.tenant_id and id = new.source_order_item_id;
|
||||
if source_version_id is distinct from new.offering_version_id then
|
||||
raise exception 'subscription item must match its order item snapshot'
|
||||
using errcode = '23514', constraint = 'ck_tenant_saas_subscription_item_order_snapshot';
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
do $guard$
|
||||
begin
|
||||
if to_regclass('tenant_saas_subscriptions') is not null then
|
||||
execute 'create trigger trg_tenant_saas_subscriptions_base_plan
|
||||
before insert or update on tenant_saas_subscriptions
|
||||
for each row execute function tiku_guard_saas_subscription_offering_type()';
|
||||
end if;
|
||||
if to_regclass('tenant_saas_subscription_items') is not null then
|
||||
execute 'create trigger trg_tenant_saas_subscription_items_offering_type
|
||||
before insert or update on tenant_saas_subscription_items
|
||||
for each row execute function tiku_guard_saas_subscription_offering_type()';
|
||||
end if;
|
||||
end
|
||||
$guard$;
|
||||
""";
|
||||
|
||||
public const string DropOfferingVersionImmutabilityGuards = """
|
||||
do $guard$
|
||||
begin
|
||||
if to_regclass('saas_offering_version_features') is not null then
|
||||
execute 'drop trigger if exists trg_saas_offering_version_features_published_immutable on saas_offering_version_features';
|
||||
end if;
|
||||
|
||||
if to_regclass('saas_offering_version_limits') is not null then
|
||||
execute 'drop trigger if exists trg_saas_offering_version_limits_published_immutable on saas_offering_version_limits';
|
||||
end if;
|
||||
|
||||
if to_regclass('saas_offering_versions') is not null then
|
||||
execute 'drop trigger if exists trg_saas_offering_versions_published_immutable on saas_offering_versions';
|
||||
end if;
|
||||
|
||||
if to_regclass('tenant_saas_subscription_items') is not null then
|
||||
execute 'drop trigger if exists trg_tenant_saas_subscription_items_offering_type on tenant_saas_subscription_items';
|
||||
end if;
|
||||
|
||||
if to_regclass('tenant_saas_subscriptions') is not null then
|
||||
execute 'drop trigger if exists trg_tenant_saas_subscriptions_base_plan on tenant_saas_subscriptions';
|
||||
end if;
|
||||
end
|
||||
$guard$;
|
||||
|
||||
drop function if exists tiku_guard_saas_offering_version_child_immutable();
|
||||
drop function if exists tiku_guard_saas_offering_version_immutable();
|
||||
drop function if exists tiku_guard_saas_subscription_offering_type();
|
||||
""";
|
||||
}
|
||||
@@ -132,8 +132,6 @@ public sealed class TikuDbContext(
|
||||
public DbSet<ActivationCode> ActivationCodes => Set<ActivationCode>();
|
||||
public DbSet<Coupon> Coupons => Set<Coupon>();
|
||||
public DbSet<CouponRedemption> CouponRedemptions => Set<CouponRedemption>();
|
||||
public DbSet<TenantSubscription> TenantSubscriptions => Set<TenantSubscription>();
|
||||
public DbSet<TenantUsageRecord> TenantUsageRecords => Set<TenantUsageRecord>();
|
||||
public DbSet<CommerceRefundRequest> CommerceRefundRequests => Set<CommerceRefundRequest>();
|
||||
public DbSet<CommerceRefundEvent> CommerceRefundEvents => Set<CommerceRefundEvent>();
|
||||
public DbSet<CommerceReconciliationBatch> CommerceReconciliationBatches => Set<CommerceReconciliationBatch>();
|
||||
@@ -180,19 +178,31 @@ public sealed class TikuDbContext(
|
||||
public DbSet<TenantContentNotification> TenantContentNotifications => Set<TenantContentNotification>();
|
||||
public DbSet<TenantThemeTemplate> TenantThemeTemplates => Set<TenantThemeTemplate>();
|
||||
public DbSet<TenantThemeConfig> TenantThemeConfigs => Set<TenantThemeConfig>();
|
||||
public DbSet<PlatformSaasPlan> PlatformSaasPlans => Set<PlatformSaasPlan>();
|
||||
public DbSet<ProductModule> ProductModules => Set<ProductModule>();
|
||||
public DbSet<PlanModuleEntitlement> PlanModuleEntitlements => Set<PlanModuleEntitlement>();
|
||||
public DbSet<TenantModuleOverride> TenantModuleOverrides => Set<TenantModuleOverride>();
|
||||
public DbSet<TenantBillingProfile> TenantBillingProfiles => Set<TenantBillingProfile>();
|
||||
public DbSet<TenantInvoice> TenantInvoices => Set<TenantInvoice>();
|
||||
public DbSet<TenantInvoiceItem> TenantInvoiceItems => Set<TenantInvoiceItem>();
|
||||
public DbSet<TenantInvoicePayment> TenantInvoicePayments => Set<TenantInvoicePayment>();
|
||||
public DbSet<TenantInvoiceReminder> TenantInvoiceReminders => Set<TenantInvoiceReminder>();
|
||||
public DbSet<PlatformBillingInvoiceReminder> PlatformBillingInvoiceReminders => Set<PlatformBillingInvoiceReminder>();
|
||||
public DbSet<PlatformAuditAlertRule> PlatformAuditAlertRules => Set<PlatformAuditAlertRule>();
|
||||
public DbSet<PlatformAuditAlert> PlatformAuditAlerts => Set<PlatformAuditAlert>();
|
||||
public DbSet<PlatformDunningNotificationChannel> PlatformDunningNotificationChannels => Set<PlatformDunningNotificationChannel>();
|
||||
public DbSet<PlatformDunningNotificationEvent> PlatformDunningNotificationEvents => Set<PlatformDunningNotificationEvent>();
|
||||
public DbSet<PlatformBillingDunningNotificationChannel> PlatformBillingDunningNotificationChannels => Set<PlatformBillingDunningNotificationChannel>();
|
||||
public DbSet<PlatformBillingDunningNotificationEvent> PlatformBillingDunningNotificationEvents => Set<PlatformBillingDunningNotificationEvent>();
|
||||
public DbSet<SaasFeature> SaasFeatures => Set<SaasFeature>();
|
||||
public DbSet<PermissionModule> PermissionModules => Set<PermissionModule>();
|
||||
public DbSet<SaasOffering> SaasOfferings => Set<SaasOffering>();
|
||||
public DbSet<SaasOfferingVersion> SaasOfferingVersions => Set<SaasOfferingVersion>();
|
||||
public DbSet<SaasOfferingVersionFeature> SaasOfferingVersionFeatures => Set<SaasOfferingVersionFeature>();
|
||||
public DbSet<SaasFeatureLimitDefinition> SaasFeatureLimitDefinitions => Set<SaasFeatureLimitDefinition>();
|
||||
public DbSet<SaasOfferingVersionLimit> SaasOfferingVersionLimits => Set<SaasOfferingVersionLimit>();
|
||||
public DbSet<TenantSaasSubscription> TenantSaasSubscriptions => Set<TenantSaasSubscription>();
|
||||
public DbSet<TenantSaasSubscriptionItem> TenantSaasSubscriptionItems => Set<TenantSaasSubscriptionItem>();
|
||||
public DbSet<TenantFeatureOverride> TenantFeatureOverrides => Set<TenantFeatureOverride>();
|
||||
public DbSet<TenantFeatureUsage> TenantFeatureUsages => Set<TenantFeatureUsage>();
|
||||
public DbSet<PlatformBillingQuote> PlatformBillingQuotes => Set<PlatformBillingQuote>();
|
||||
public DbSet<PlatformBillingQuoteItem> PlatformBillingQuoteItems => Set<PlatformBillingQuoteItem>();
|
||||
public DbSet<PlatformBillingOrder> PlatformBillingOrders => Set<PlatformBillingOrder>();
|
||||
public DbSet<PlatformBillingOrderItem> PlatformBillingOrderItems => Set<PlatformBillingOrderItem>();
|
||||
public DbSet<PlatformBillingPayment> PlatformBillingPayments => Set<PlatformBillingPayment>();
|
||||
public DbSet<PlatformBillingPaymentEvent> PlatformBillingPaymentEvents => Set<PlatformBillingPaymentEvent>();
|
||||
public DbSet<PlatformBillingRefund> PlatformBillingRefunds => Set<PlatformBillingRefund>();
|
||||
public DbSet<PlatformBillingInvoice> PlatformBillingInvoices => Set<PlatformBillingInvoice>();
|
||||
public DbSet<PocketBaseImportRun> PocketBaseImportRuns => Set<PocketBaseImportRun>();
|
||||
public DbSet<PocketBaseRawRecord> PocketBaseRawRecords => Set<PocketBaseRawRecord>();
|
||||
public DbSet<PocketBaseImportIssue> PocketBaseImportIssues => Set<PocketBaseImportIssue>();
|
||||
|
||||
Reference in New Issue
Block a user