feat: complete SaaS commercial delivery workflows

This commit is contained in:
2026-08-01 15:26:21 +08:00
parent 46abf4d62f
commit d58bcd97e9
56 changed files with 27505 additions and 181 deletions

View File

@@ -28,6 +28,50 @@ internal sealed class TenantBillingProfileConfiguration : IEntityTypeConfigurati
}
}
internal sealed class TenantBillingPolicyConfiguration : IEntityTypeConfiguration<TenantBillingPolicy>
{
public void Configure(EntityTypeBuilder<TenantBillingPolicy> builder)
{
builder.ToTable("tenant_billing_policies");
builder.HasKey(entity => entity.TenantId);
builder.ConfigureTimestamps();
builder.Property(entity => entity.CollectionMode).HasSnakeCaseEnum();
builder.Property(entity => entity.DefaultPaymentProvider).HasMaxLength(50);
builder.ToTable(table => table.HasCheckConstraint(
"ck_tenant_billing_policies_renewal_lead_days",
"renewal_lead_days between 1 and 90"));
builder.HasOne<Tenant>().WithOne().HasForeignKey<TenantBillingPolicy>(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
}
}
internal sealed class TenantOwnerActivationGrantConfiguration : IEntityTypeConfiguration<TenantOwnerActivationGrant>
{
public void Configure(EntityTypeBuilder<TenantOwnerActivationGrant> builder)
{
builder.ConfigureTenantEntity("tenant_owner_activation_grants");
builder.ConfigureTimestamps();
builder.Property(entity => entity.TokenHash).HasMaxLength(64);
builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.ConsumedAt, entity.ExpiresAt });
builder.HasIndex(entity => entity.TokenHash).IsUnique().HasAnnotation("Tiku:GlobalUnique", true);
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.CreatedBy).OnDelete(DeleteBehavior.Restrict);
}
}
internal sealed class PlatformOperationIdempotencyConfiguration : IEntityTypeConfiguration<PlatformOperationIdempotency>
{
public void Configure(EntityTypeBuilder<PlatformOperationIdempotency> builder)
{
builder.ConfigureEntity("platform_operation_idempotencies");
builder.ConfigureTimestamps();
builder.Property(entity => entity.Scope).HasMaxLength(100);
builder.Property(entity => entity.IdempotencyKey).HasMaxLength(200);
builder.Property(entity => entity.RequestHash).HasMaxLength(64);
builder.HasIndex(entity => new { entity.ActorUserId, entity.Scope, entity.IdempotencyKey }).IsUnique();
builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.ActorUserId).OnDelete(DeleteBehavior.Restrict);
}
}
internal sealed class PlatformBillingInvoiceReminderConfiguration : IEntityTypeConfiguration<PlatformBillingInvoiceReminder>
{
public void Configure(EntityTypeBuilder<PlatformBillingInvoiceReminder> builder)

View File

@@ -323,7 +323,12 @@ internal sealed class PlatformBillingRefundConfiguration : IEntityTypeConfigurat
builder.Property(value => value.Status).HasSnakeCaseEnum();
builder.Property(value => value.Reason).HasMaxLength(1000);
builder.Property(value => value.ProviderRefundNo).HasMaxLength(200);
builder.Property(value => value.IdempotencyKey).HasMaxLength(200);
builder.Property(value => value.SubscriptionEffect).HasSnakeCaseEnum();
builder.Property(value => value.ReviewReason).HasMaxLength(1000);
builder.Property(value => value.LastError).HasMaxLength(2000);
builder.HasIndex(value => new { value.TenantId, value.RefundNo }).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.Restrict);
builder.HasOne<PlatformBillingPayment>().WithMany().HasForeignKey(value => new { value.TenantId, value.PaymentId }).HasPrincipalKey(value => new { value.TenantId, value.Id }).OnDelete(DeleteBehavior.Restrict);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Tiku.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class CommercialDeliveryClosure : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "idempotency_key",
table: "platform_billing_refunds",
type: "character varying(200)",
maxLength: 200,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "last_error",
table: "platform_billing_refunds",
type: "character varying(2000)",
maxLength: 2000,
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "requested_by",
table: "platform_billing_refunds",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "review_reason",
table: "platform_billing_refunds",
type: "character varying(1000)",
maxLength: 1000,
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "reviewed_at",
table: "platform_billing_refunds",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "reviewed_by",
table: "platform_billing_refunds",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "subscription_effect",
table: "platform_billing_refunds",
type: "character varying(32)",
maxLength: 32,
nullable: true);
migrationBuilder.Sql(
"""
UPDATE platform_billing_refunds
SET idempotency_key = 'legacy:' || id::text,
subscription_effect = 'keep_service'
WHERE idempotency_key IS NULL OR subscription_effect IS NULL;
""");
migrationBuilder.AlterColumn<string>(
name: "idempotency_key",
table: "platform_billing_refunds",
type: "character varying(200)",
maxLength: 200,
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(200)",
oldMaxLength: 200,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "subscription_effect",
table: "platform_billing_refunds",
type: "character varying(32)",
maxLength: 32,
nullable: false,
oldClrType: typeof(string),
oldType: "character varying(32)",
oldMaxLength: 32,
oldNullable: true);
migrationBuilder.CreateTable(
name: "platform_operation_idempotencies",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
actor_user_id = table.Column<Guid>(type: "uuid", nullable: false),
scope = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
idempotency_key = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
request_hash = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
resource_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_platform_operation_idempotencies", x => x.id);
table.ForeignKey(
name: "fk_platform_operation_idempotencies_users_actor_user_id",
column: x => x.actor_user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "tenant_billing_policies",
columns: table => new
{
tenant_id = table.Column<Guid>(type: "uuid", nullable: false),
collection_mode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
default_payment_provider = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
auto_generate_renewal = table.Column<bool>(type: "boolean", nullable: false),
renewal_lead_days = 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_tenant_billing_policies", x => x.tenant_id);
table.CheckConstraint("ck_tenant_billing_policies_renewal_lead_days", "renewal_lead_days between 1 and 90");
table.ForeignKey(
name: "fk_tenant_billing_policies_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "tenant_owner_activation_grants",
columns: table => new
{
id = table.Column<Guid>(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"),
user_id = table.Column<Guid>(type: "uuid", nullable: false),
created_by = table.Column<Guid>(type: "uuid", nullable: false),
token_hash = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
expires_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
consumed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", 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_owner_activation_grants", x => x.id);
table.UniqueConstraint("ak_tenant_owner_activation_grants_tenant_id_id", x => new { x.tenant_id, x.id });
table.ForeignKey(
name: "fk_tenant_owner_activation_grants_tenants_tenant_id",
column: x => x.tenant_id,
principalTable: "tenants",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "fk_tenant_owner_activation_grants_users_created_by",
column: x => x.created_by,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "fk_tenant_owner_activation_grants_users_user_id",
column: x => x.user_id,
principalTable: "users",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_platform_billing_refunds_tenant_id_idempotency_key",
table: "platform_billing_refunds",
columns: new[] { "tenant_id", "idempotency_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_platform_operation_idempotencies_actor_user_id_scope_idempo~",
table: "platform_operation_idempotencies",
columns: new[] { "actor_user_id", "scope", "idempotency_key" },
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_owner_activation_grants_created_by",
table: "tenant_owner_activation_grants",
column: "created_by");
migrationBuilder.CreateIndex(
name: "ix_tenant_owner_activation_grants_tenant_id_user_id_consumed_a~",
table: "tenant_owner_activation_grants",
columns: new[] { "tenant_id", "user_id", "consumed_at", "expires_at" });
migrationBuilder.CreateIndex(
name: "ix_tenant_owner_activation_grants_token_hash",
table: "tenant_owner_activation_grants",
column: "token_hash",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_tenant_owner_activation_grants_user_id",
table: "tenant_owner_activation_grants",
column: "user_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "platform_operation_idempotencies");
migrationBuilder.DropTable(
name: "tenant_billing_policies");
migrationBuilder.DropTable(
name: "tenant_owner_activation_grants");
migrationBuilder.DropIndex(
name: "ix_platform_billing_refunds_tenant_id_idempotency_key",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "idempotency_key",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "last_error",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "requested_by",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "review_reason",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "reviewed_at",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "reviewed_by",
table: "platform_billing_refunds");
migrationBuilder.DropColumn(
name: "subscription_effect",
table: "platform_billing_refunds");
}
}
}

View File

@@ -13203,6 +13203,17 @@ namespace Tiku.Infrastructure.Persistence.Migrations
.HasColumnName("created_at")
.HasDefaultValueSql("now()");
b.Property<string>("IdempotencyKey")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)")
.HasColumnName("idempotency_key");
b.Property<string>("LastError")
.HasMaxLength(2000)
.HasColumnType("character varying(2000)")
.HasColumnName("last_error");
b.Property<Guid>("OrderId")
.HasColumnType("uuid")
.HasColumnName("order_id");
@@ -13228,12 +13239,35 @@ namespace Tiku.Infrastructure.Persistence.Migrations
.HasColumnType("character varying(100)")
.HasColumnName("refund_no");
b.Property<Guid?>("RequestedBy")
.HasColumnType("uuid")
.HasColumnName("requested_by");
b.Property<string>("ReviewReason")
.HasMaxLength(1000)
.HasColumnType("character varying(1000)")
.HasColumnName("review_reason");
b.Property<DateTimeOffset?>("ReviewedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("reviewed_at");
b.Property<Guid?>("ReviewedBy")
.HasColumnType("uuid")
.HasColumnName("reviewed_by");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("status");
b.Property<string>("SubscriptionEffect")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("subscription_effect");
b.Property<Guid>("TenantId")
.HasColumnType("uuid")
.HasColumnName("tenant_id");
@@ -13250,6 +13284,10 @@ namespace Tiku.Infrastructure.Persistence.Migrations
b.HasAlternateKey("TenantId", "Id")
.HasName("ak_platform_billing_refunds_tenant_id_id");
b.HasIndex("TenantId", "IdempotencyKey")
.IsUnique()
.HasDatabaseName("ix_platform_billing_refunds_tenant_id_idempotency_key");
b.HasIndex("TenantId", "PaymentId")
.HasDatabaseName("ix_platform_billing_refunds_tenant_id_payment_id");
@@ -13266,6 +13304,62 @@ namespace Tiku.Infrastructure.Persistence.Migrations
});
});
modelBuilder.Entity("Tiku.Domain.Platform.PlatformOperationIdempotency", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id")
.HasDefaultValueSql("gen_random_uuid()");
b.Property<Guid>("ActorUserId")
.HasColumnType("uuid")
.HasColumnName("actor_user_id");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at")
.HasDefaultValueSql("now()");
b.Property<string>("IdempotencyKey")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)")
.HasColumnName("idempotency_key");
b.Property<string>("RequestHash")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("request_hash");
b.Property<Guid>("ResourceId")
.HasColumnType("uuid")
.HasColumnName("resource_id");
b.Property<string>("Scope")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("scope");
b.Property<DateTimeOffset>("UpdatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at")
.HasDefaultValueSql("now()");
b.HasKey("Id")
.HasName("pk_platform_operation_idempotencies");
b.HasIndex("ActorUserId", "Scope", "IdempotencyKey")
.IsUnique()
.HasDatabaseName("ix_platform_operation_idempotencies_actor_user_id_scope_idempo~");
b.ToTable("platform_operation_idempotencies", (string)null);
});
modelBuilder.Entity("Tiku.Domain.Platform.PlatformPaymentApp", b =>
{
b.Property<Guid>("Id")
@@ -13809,6 +13903,53 @@ namespace Tiku.Infrastructure.Persistence.Migrations
});
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingPolicy", b =>
{
b.Property<Guid>("TenantId")
.HasColumnType("uuid")
.HasColumnName("tenant_id");
b.Property<bool>("AutoGenerateRenewal")
.HasColumnType("boolean")
.HasColumnName("auto_generate_renewal");
b.Property<string>("CollectionMode")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("collection_mode");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at")
.HasDefaultValueSql("now()");
b.Property<string>("DefaultPaymentProvider")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("default_payment_provider");
b.Property<int>("RenewalLeadDays")
.HasColumnType("integer")
.HasColumnName("renewal_lead_days");
b.Property<DateTimeOffset>("UpdatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at")
.HasDefaultValueSql("now()");
b.HasKey("TenantId")
.HasName("pk_tenant_billing_policies");
b.ToTable("tenant_billing_policies", null, t =>
{
t.HasCheckConstraint("ck_tenant_billing_policies_renewal_lead_days", "renewal_lead_days between 1 and 90");
});
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b =>
{
b.Property<Guid>("TenantId")
@@ -14026,6 +14167,75 @@ namespace Tiku.Infrastructure.Persistence.Migrations
});
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantOwnerActivationGrant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("id")
.HasDefaultValueSql("gen_random_uuid()");
b.Property<DateTimeOffset?>("ConsumedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("consumed_at");
b.Property<DateTimeOffset>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("created_at")
.HasDefaultValueSql("now()");
b.Property<Guid>("CreatedBy")
.HasColumnType("uuid")
.HasColumnName("created_by");
b.Property<DateTimeOffset>("ExpiresAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("expires_at");
b.Property<Guid>("TenantId")
.HasColumnType("uuid")
.HasColumnName("tenant_id");
b.Property<string>("TokenHash")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)")
.HasColumnName("token_hash");
b.Property<DateTimeOffset>("UpdatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasColumnName("updated_at")
.HasDefaultValueSql("now()");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("pk_tenant_owner_activation_grants");
b.HasAlternateKey("TenantId", "Id")
.HasName("ak_tenant_owner_activation_grants_tenant_id_id");
b.HasIndex("CreatedBy")
.HasDatabaseName("ix_tenant_owner_activation_grants_created_by");
b.HasIndex("TokenHash")
.IsUnique()
.HasDatabaseName("ix_tenant_owner_activation_grants_token_hash")
.HasAnnotation("Tiku:GlobalUnique", true);
b.HasIndex("UserId")
.HasDatabaseName("ix_tenant_owner_activation_grants_user_id");
b.HasIndex("TenantId", "UserId", "ConsumedAt", "ExpiresAt")
.HasDatabaseName("ix_tenant_owner_activation_grants_tenant_id_user_id_consumed_a~");
b.ToTable("tenant_owner_activation_grants", (string)null);
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantSaasSubscription", b =>
{
b.Property<Guid>("Id")
@@ -19563,6 +19773,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations
.HasConstraintName("fk_platform_billing_refunds_platform_billing_payments_tenant_i~");
});
modelBuilder.Entity("Tiku.Domain.Platform.PlatformOperationIdempotency", b =>
{
b.HasOne("Tiku.Domain.Identity.User", null)
.WithMany()
.HasForeignKey("ActorUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_platform_operation_idempotencies_users_actor_user_id");
});
modelBuilder.Entity("Tiku.Domain.Platform.PlatformPaymentChannel", b =>
{
b.HasOne("Tiku.Domain.Platform.PlatformPaymentApp", null)
@@ -19630,6 +19850,16 @@ namespace Tiku.Infrastructure.Persistence.Migrations
.HasConstraintName("fk_saas_offering_version_limits_saas_offering_versions_offerin~");
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingPolicy", b =>
{
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
.WithOne()
.HasForeignKey("Tiku.Domain.Platform.TenantBillingPolicy", "TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_tenant_billing_policies_tenants_tenant_id");
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantBillingProfile", b =>
{
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
@@ -19676,6 +19906,30 @@ namespace Tiku.Infrastructure.Persistence.Migrations
.HasConstraintName("fk_tenant_feature_usage_tenants_tenant_id");
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantOwnerActivationGrant", b =>
{
b.HasOne("Tiku.Domain.Identity.User", null)
.WithMany()
.HasForeignKey("CreatedBy")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_tenant_owner_activation_grants_users_created_by");
b.HasOne("Tiku.Domain.Tenancy.Tenant", null)
.WithMany()
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_tenant_owner_activation_grants_tenants_tenant_id");
b.HasOne("Tiku.Domain.Identity.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_tenant_owner_activation_grants_users_user_id");
});
modelBuilder.Entity("Tiku.Domain.Platform.TenantSaasSubscription", b =>
{
b.HasOne("Tiku.Domain.Platform.SaasOfferingVersion", null)

View File

@@ -185,6 +185,9 @@ public sealed class TikuDbContext(
public DbSet<TenantThemeTemplate> TenantThemeTemplates => Set<TenantThemeTemplate>();
public DbSet<TenantThemeConfig> TenantThemeConfigs => Set<TenantThemeConfig>();
public DbSet<TenantBillingProfile> TenantBillingProfiles => Set<TenantBillingProfile>();
public DbSet<TenantBillingPolicy> TenantBillingPolicies => Set<TenantBillingPolicy>();
public DbSet<TenantOwnerActivationGrant> TenantOwnerActivationGrants => Set<TenantOwnerActivationGrant>();
public DbSet<PlatformOperationIdempotency> PlatformOperationIdempotencies => Set<PlatformOperationIdempotency>();
public DbSet<PlatformBillingInvoiceReminder> PlatformBillingInvoiceReminders => Set<PlatformBillingInvoiceReminder>();
public DbSet<PlatformAuditAlertRule> PlatformAuditAlertRules => Set<PlatformAuditAlertRule>();
public DbSet<PlatformAuditAlert> PlatformAuditAlerts => Set<PlatformAuditAlert>();