Skip to content

Commit edc9f2d

Browse files
committed
Collapse five back-office migrations into one
1 parent ee907c8 commit edc9f2d

6 files changed

Lines changed: 94 additions & 122 deletions

application/account/Core/Database/Migrations/20260503120000_AddSubscriptionTracking.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.

application/account/Core/Database/Migrations/20260503130000_BackfillSubscribedSince.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

application/account/Core/Database/Migrations/20260505140000_AddBillingEvents.cs

Lines changed: 0 additions & 45 deletions
This file was deleted.

application/account/Core/Database/Migrations/20260506180000_AddSubscriptionDriftFields.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

application/account/Core/Database/Migrations/20260507205500_AddPaymentTransactionTaxBreakdownConstraint.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using Microsoft.EntityFrameworkCore.Infrastructure;
2+
using Microsoft.EntityFrameworkCore.Migrations;
3+
4+
namespace Account.Database.Migrations;
5+
6+
[DbContext(typeof(AccountDbContext))]
7+
[Migration("20260508021500_AddBillingEventsAndDriftDetection")]
8+
public sealed class AddBillingEventsAndDriftDetection : Migration
9+
{
10+
protected override void Up(MigrationBuilder migrationBuilder)
11+
{
12+
migrationBuilder.AddColumn<DateTimeOffset>("subscribed_since", "subscriptions", "timestamptz", nullable: true);
13+
migrationBuilder.AddColumn<decimal>("scheduled_price_amount", "subscriptions", "numeric(18,2)", nullable: true);
14+
migrationBuilder.AddColumn<bool>("has_drift_detected", "subscriptions", "boolean", nullable: false, defaultValue: false);
15+
migrationBuilder.AddColumn<DateTimeOffset>("drift_checked_at", "subscriptions", "timestamptz", nullable: true);
16+
migrationBuilder.AddColumn<string>("drift_discrepancies", "subscriptions", "jsonb", nullable: false, defaultValue: "[]");
17+
18+
migrationBuilder.CreateIndex("ix_subscriptions_has_drift_detected", "subscriptions", "has_drift_detected", filter: "has_drift_detected = true");
19+
20+
// Subscriptions created before this migration have no subscribed_since because the column did not
21+
// exist when the Basis -> paid transition occurred. Best available proxy for the start of their paid
22+
// run is the subscription row's created_at timestamp. Only backfill active paid subscriptions
23+
// (those that have a Stripe subscription id and are not on the free Basis plan).
24+
migrationBuilder.Sql(
25+
"""
26+
UPDATE subscriptions
27+
SET subscribed_since = created_at
28+
WHERE subscribed_since IS NULL
29+
AND stripe_subscription_id IS NOT NULL
30+
AND plan <> 'Basis';
31+
"""
32+
);
33+
34+
// PaymentTransaction.AmountExcludingTax and TaxAmount became non-nullable in the C# domain alongside
35+
// this migration. Existing rows synced from Stripe before that change may have those keys missing or
36+
// null. Default AmountExcludingTax to the gross Amount and TaxAmount to 0 so the CHECK constraint
37+
// below passes. The next Stripe sync per tenant overwrites these with the real breakdown.
38+
migrationBuilder.Sql(
39+
"""
40+
UPDATE subscriptions
41+
SET payment_transactions = (
42+
SELECT jsonb_agg(
43+
e || jsonb_build_object(
44+
'AmountExcludingTax', COALESCE((e->>'AmountExcludingTax')::numeric, (e->>'Amount')::numeric, 0),
45+
'TaxAmount', COALESCE((e->>'TaxAmount')::numeric, 0)
46+
)
47+
)
48+
FROM jsonb_array_elements(payment_transactions) e
49+
)
50+
WHERE jsonb_array_length(payment_transactions) > 0
51+
AND jsonb_path_exists(payment_transactions, '$[*] ? (!(@.AmountExcludingTax.type() == "number") || !(@.TaxAmount.type() == "number"))');
52+
"""
53+
);
54+
55+
migrationBuilder.AddCheckConstraint(
56+
"chk_subscriptions_payment_transactions_tax_breakdown",
57+
"subscriptions",
58+
"""NOT jsonb_path_exists(payment_transactions, '$[*] ? (!(@.AmountExcludingTax.type() == "number") || !(@.TaxAmount.type() == "number"))')"""
59+
);
60+
61+
migrationBuilder.CreateTable(
62+
"billing_events",
63+
table => new
64+
{
65+
tenant_id = table.Column<long>("bigint", nullable: false),
66+
id = table.Column<string>("text", nullable: false),
67+
subscription_id = table.Column<string>("text", nullable: false),
68+
created_at = table.Column<DateTimeOffset>("timestamptz", nullable: false),
69+
modified_at = table.Column<DateTimeOffset>("timestamptz", nullable: true),
70+
event_type = table.Column<string>("text", nullable: false),
71+
from_plan = table.Column<string>("text", nullable: true),
72+
to_plan = table.Column<string>("text", nullable: true),
73+
previous_amount = table.Column<decimal>("numeric(18,2)", nullable: true),
74+
new_amount = table.Column<decimal>("numeric(18,2)", nullable: true),
75+
amount_delta = table.Column<decimal>("numeric(18,2)", nullable: true),
76+
currency = table.Column<string>("text", nullable: true),
77+
days_on_previous_plan = table.Column<int>("integer", nullable: true),
78+
days_until_effective = table.Column<int>("integer", nullable: true),
79+
days_since_cancelled = table.Column<int>("integer", nullable: true),
80+
scheduled_for = table.Column<DateTimeOffset>("timestamptz", nullable: true),
81+
effective_at = table.Column<DateTimeOffset>("timestamptz", nullable: true),
82+
occurred_at = table.Column<DateTimeOffset>("timestamptz", nullable: false),
83+
cancellation_reason = table.Column<string>("text", nullable: true),
84+
suspension_reason = table.Column<string>("text", nullable: true),
85+
stripe_reference = table.Column<string>("text", nullable: false)
86+
},
87+
constraints: table => { table.PrimaryKey("pk_billing_events", x => x.id); }
88+
);
89+
90+
migrationBuilder.CreateIndex("ix_billing_events_tenant_id_occurred_at", "billing_events", ["tenant_id", "occurred_at"], descending: [false, true]);
91+
migrationBuilder.CreateIndex("ix_billing_events_occurred_at", "billing_events", "occurred_at", descending: [true]);
92+
migrationBuilder.CreateIndex("ix_billing_events_subscription_id", "billing_events", "subscription_id");
93+
}
94+
}

0 commit comments

Comments
 (0)