Files
cfw-autumn/shared/models/cusModels/invoiceModels/invoiceLineItemTable.ts
2026-03-02 10:43:01 +00:00

92 lines
3.6 KiB
TypeScript

import type { InferInsertModel, InferSelectModel } from "drizzle-orm";
import {
boolean,
foreignKey,
jsonb,
numeric,
pgTable,
text,
unique,
} from "drizzle-orm/pg-core";
import { collatePgColumn, sqlNow } from "../../../db/utils.js";
import type { InvoiceLineItemDiscount } from "./invoiceLineItemModels.js";
import { invoices } from "./invoiceTable.js";
export const invoiceLineItems = pgTable(
"invoice_line_items",
{
id: text("id").primaryKey(),
created_at: numeric({ mode: "number" }).notNull().default(sqlNow),
invoice_id: text("invoice_id"), // Nullable for deferred/pending line items
// Stripe identifiers
stripe_id: text("stripe_id"), // Stripe invoice line item ID (il_xxx)
stripe_invoice_id: text("stripe_invoice_id"), // Stripe invoice ID
stripe_invoice_item_id: text("stripe_invoice_item_id"), // Original Stripe invoice item ID (ii_xxx) for linking deferred items
stripe_subscription_item_id: text("stripe_subscription_item_id"), // Groups tiered line items
stripe_product_id: text("stripe_product_id"),
stripe_price_id: text("stripe_price_id"),
stripe_discountable: boolean("stripe_discountable").notNull().default(true),
// Amounts
amount: numeric({ mode: "number" }).notNull(), // Pre-discount amount
amount_after_discounts: numeric({ mode: "number" }).notNull(), // Post-discount amount
currency: text("currency").notNull().default("usd"),
// Quantities
stripe_quantity: numeric({ mode: "number" }), // Raw Stripe quantity
total_quantity: numeric({ mode: "number" }), // Total usage (stripe_quantity * billing_units)
paid_quantity: numeric({ mode: "number" }), // Quantity being charged (overage)
// Description & metadata
description: text("description").notNull(),
description_source: text("description_source"), // "stripe" or "autumn" - where description came from
direction: text("direction").notNull(), // "charge" or "refund"
billing_timing: text("billing_timing"), // "in_advance" or "in_arrear"
prorated: boolean("prorated").notNull().default(false),
// Autumn entity relationships
price_id: text("price_id"), // External Autumn price ID
customer_product_ids: jsonb("customer_product_ids")
.$type<string[]>()
.notNull()
.default([]), // Array of customer_product IDs (multi-entity support)
customer_price_ids: jsonb("customer_price_ids")
.$type<string[]>()
.notNull()
.default([]), // Array of customer_price IDs (multi-entity support)
customer_entitlement_ids: jsonb("customer_entitlement_ids")
.$type<string[]>()
.notNull()
.default([]), // Array of customer_entitlement IDs (multi-entity support)
internal_product_id: text("internal_product_id"), // Internal product ID
product_id: text("product_id"), // External product ID
internal_feature_id: text("internal_feature_id"), // Internal feature ID
feature_id: text("feature_id"), // External feature ID
// Billing periods
effective_period_start: numeric({ mode: "number" }), // Billing period start (ms)
effective_period_end: numeric({ mode: "number" }), // Billing period end (ms)
// Discounts
discounts: jsonb("discounts")
.$type<InvoiceLineItemDiscount>()
.array()
.default([]),
},
(table) => [
foreignKey({
columns: [table.invoice_id],
foreignColumns: [invoices.id],
name: "invoice_line_items_invoice_id_fkey",
}).onDelete("cascade"),
// Unique partial index on stripe_id for upsert support
unique("invoice_line_items_stripe_id_unique").on(table.stripe_id),
],
);
collatePgColumn(invoiceLineItems.id, "C");
export type DbInvoiceLineItem = InferSelectModel<typeof invoiceLineItems>;
export type InsertDbInvoiceLineItem = InferInsertModel<typeof invoiceLineItems>;