feat: billing/invoice details (full name, address, phone) under Settings > Billing (v0.76.0)
Adds a new user_billing_details table (1:1 with users, separate from the display name) with a form and GET/PUT API route. Full name is required by the form/API; address and phone stay optional. Not wired into Stripe invoicing yet — just captured for future use. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE "user_billing_details" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"full_name" text,
|
||||
"address_line1" text,
|
||||
"address_line2" text,
|
||||
"city" text,
|
||||
"postal_code" text,
|
||||
"country" text,
|
||||
"phone" text,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "user_billing_details_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "user_billing_details" ADD CONSTRAINT "user_billing_details_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -442,6 +442,13 @@
|
||||
"when": 1784805788971,
|
||||
"tag": "0062_mean_ikaris",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 63,
|
||||
"version": "7",
|
||||
"when": 1784885406472,
|
||||
"tag": "0063_sharp_stone_men",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { users } from "./users";
|
||||
|
||||
// Dedup log for inbound Stripe webhook events. Stripe may redeliver the same
|
||||
// event within its retry/tolerance window; we insert the event id here
|
||||
@@ -9,3 +11,25 @@ export const processedStripeEvents = pgTable("processed_stripe_events", {
|
||||
type: text("type").notNull(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
// Billing/invoice details, separate from the display `name` on `users` —
|
||||
// fullName here is the legal/billing name used on invoices. All columns are
|
||||
// nullable at the DB level (existing users have none of this filled in yet);
|
||||
// fullName being "mandatory" is enforced by the API/form, not a NOT NULL
|
||||
// constraint, since we can't backfill it for every existing user.
|
||||
export const userBillingDetails = pgTable("user_billing_details", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }).unique(),
|
||||
fullName: text("full_name"),
|
||||
addressLine1: text("address_line1"),
|
||||
addressLine2: text("address_line2"),
|
||||
city: text("city"),
|
||||
postalCode: text("postal_code"),
|
||||
country: text("country"),
|
||||
phone: text("phone"),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const userBillingDetailsRelations = relations(userBillingDetails, ({ one }) => ({
|
||||
user: one(users, { fields: [userBillingDetails.userId], references: [users.id] }),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user